From 52e8c99022015da50c0b9f5b1df4f1623f011dde Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Sat, 16 May 2026 19:32:11 -0400 Subject: [PATCH] Phase 1: module interface + registry + top-level dispatcher - core/module.h: struct iamroot_module + iamroot_result_t - core/registry.{h,c}: flat-array module registry with find-by-name - modules/copy_fail_family/iamroot_modules.{h,c}: bridge layer exposing 5 modules (copy_fail, copy_fail_gcm, dirty_frag_esp, dirty_frag_esp6, dirty_frag_rxrpc) wired to the absorbed DIRTYFAIL detect/exploit functions; df_result_t/iamroot_result_t share numeric values intentionally for zero-cost translation - iamroot.c: top-level CLI dispatcher with --scan / --list / --exploit / --mitigate / --cleanup, JSON output, --i-know gate - Restored modules/copy_fail_family/src/ structure (DIRTYFAIL Makefile expects it; the initial flat copy broke that contract) - Top-level Makefile builds one binary; filters out DIRTYFAIL's original dirtyfail.c main so it doesn't conflict with iamroot.c Verified end-to-end on kctf-mgr (Linux): clean compile, 5 modules register, --scan --json output ingest-ready, exit codes propagate. --- Makefile | 78 ++++-- core/module.h | 89 +++++++ core/registry.c | 60 +++++ core/registry.h | 25 ++ iamroot.c | 235 ++++++++++++++++++ modules/copy_fail_family/iamroot_modules.c | 176 +++++++++++++ modules/copy_fail_family/iamroot_modules.h | 28 +++ .../{ => src}/apparmor_bypass.c | 0 .../{ => src}/apparmor_bypass.h | 0 modules/copy_fail_family/{ => src}/backdoor.c | 0 modules/copy_fail_family/{ => src}/backdoor.h | 0 modules/copy_fail_family/{ => src}/common.c | 0 modules/copy_fail_family/{ => src}/common.h | 0 modules/copy_fail_family/{ => src}/copyfail.c | 0 modules/copy_fail_family/{ => src}/copyfail.h | 0 .../copy_fail_family/{ => src}/copyfail_gcm.c | 0 .../copy_fail_family/{ => src}/copyfail_gcm.h | 0 .../copy_fail_family/{ => src}/dirtyfail.c | 0 .../{ => src}/dirtyfrag_esp.c | 0 .../{ => src}/dirtyfrag_esp.h | 0 .../{ => src}/dirtyfrag_esp6.c | 0 .../{ => src}/dirtyfrag_esp6.h | 0 .../{ => src}/dirtyfrag_rxrpc.c | 0 .../{ => src}/dirtyfrag_rxrpc.h | 0 .../copy_fail_family/{ => src}/exploit_su.c | 0 .../copy_fail_family/{ => src}/exploit_su.h | 0 modules/copy_fail_family/{ => src}/fcrypt.c | 0 modules/copy_fail_family/{ => src}/fcrypt.h | 0 modules/copy_fail_family/{ => src}/mitigate.c | 0 modules/copy_fail_family/{ => src}/mitigate.h | 0 30 files changed, 673 insertions(+), 18 deletions(-) create mode 100644 core/module.h create mode 100644 core/registry.c create mode 100644 core/registry.h create mode 100644 iamroot.c create mode 100644 modules/copy_fail_family/iamroot_modules.c create mode 100644 modules/copy_fail_family/iamroot_modules.h rename modules/copy_fail_family/{ => src}/apparmor_bypass.c (100%) rename modules/copy_fail_family/{ => src}/apparmor_bypass.h (100%) rename modules/copy_fail_family/{ => src}/backdoor.c (100%) rename modules/copy_fail_family/{ => src}/backdoor.h (100%) rename modules/copy_fail_family/{ => src}/common.c (100%) rename modules/copy_fail_family/{ => src}/common.h (100%) rename modules/copy_fail_family/{ => src}/copyfail.c (100%) rename modules/copy_fail_family/{ => src}/copyfail.h (100%) rename modules/copy_fail_family/{ => src}/copyfail_gcm.c (100%) rename modules/copy_fail_family/{ => src}/copyfail_gcm.h (100%) rename modules/copy_fail_family/{ => src}/dirtyfail.c (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_esp.c (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_esp.h (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_esp6.c (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_esp6.h (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_rxrpc.c (100%) rename modules/copy_fail_family/{ => src}/dirtyfrag_rxrpc.h (100%) rename modules/copy_fail_family/{ => src}/exploit_su.c (100%) rename modules/copy_fail_family/{ => src}/exploit_su.h (100%) rename modules/copy_fail_family/{ => src}/fcrypt.c (100%) rename modules/copy_fail_family/{ => src}/fcrypt.h (100%) rename modules/copy_fail_family/{ => src}/mitigate.c (100%) rename modules/copy_fail_family/{ => src}/mitigate.h (100%) diff --git a/Makefile b/Makefile index 23daf31..6da9fbf 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,68 @@ -# IAMROOT top-level Makefile +# IAMROOT — top-level Makefile (Phase 1) # -# Phase 0 (current): defers to modules/copy_fail_family/Makefile. -# Phase 1: real dispatcher build that links all modules into one -# binary. See ROADMAP.md. +# Builds one binary `iamroot` linked from: +# - core/ module interface + registry +# - modules// one family per subdir, contributes objects to the +# final binary +# - iamroot.c top-level dispatcher +# +# Each family is currently flat (Phase 1 keeps copy_fail_family's +# absorbed DIRTYFAIL source in modules/copy_fail_family/src/). +# Future families register the same way: add their register_* call to +# iamroot.c's main() and add their src dir to MODULE_DIRS below. -MODULES := copy_fail_family +CC ?= gcc +CFLAGS ?= -O2 -Wall -Wextra -Wno-unused-parameter -Wno-pointer-arith \ + -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 +LDFLAGS ?= -.PHONY: all clean $(MODULES) +BUILD := build +BIN := iamroot -all: $(MODULES) +# core/ +CORE_SRC := core/registry.c +CORE_OBJ := $(BUILD)/core/registry.o -$(MODULES): - $(MAKE) -C modules/$@ +# Family: copy_fail_family +# All DIRTYFAIL .c files contribute; iamroot_modules.c is the bridge. +CFF_DIR := modules/copy_fail_family +CFF_SRCS := $(wildcard $(CFF_DIR)/src/*.c) $(CFF_DIR)/iamroot_modules.c +# Filter out the original dirtyfail.c (its main() conflicts with iamroot.c's main). +CFF_SRCS := $(filter-out $(CFF_DIR)/src/dirtyfail.c, $(CFF_SRCS)) +CFF_OBJS := $(patsubst %.c,$(BUILD)/%.o,$(CFF_SRCS)) + +# Top-level dispatcher +TOP_OBJ := $(BUILD)/iamroot.o + +ALL_OBJS := $(TOP_OBJ) $(CORE_OBJ) $(CFF_OBJS) + +.PHONY: all clean debug static help + +all: $(BIN) + +$(BIN): $(ALL_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + +# Generic compile: any .c → corresponding .o under build/ +$(BUILD)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -Icore -I$(CFF_DIR)/src -c -o $@ $< + +debug: CFLAGS := -O0 -g3 -Wall -Wextra -Wno-unused-parameter -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 +debug: clean $(BIN) + +static: LDFLAGS += -static +static: clean $(BIN) clean: - @for m in $(MODULES); do \ - $(MAKE) -C modules/$$m clean; \ - done - rm -rf build/ + rm -rf $(BUILD) $(BIN) -# Convenience: scan the host using the absorbed DIRTYFAIL-as-module -# until Phase 1's real dispatcher lands. -scan: - @modules/copy_fail_family/dirtyfail --scan 2>/dev/null || \ - (echo "Build the copy_fail module first: make copy_fail_family" && exit 1) +help: + @echo "Targets:" + @echo " make build optimized iamroot binary" + @echo " make debug build with -O0 -g3" + @echo " make static build a fully static binary" + @echo " make clean remove build artifacts" + @echo "" + @echo "Per-module (legacy) — not built by default:" + @echo " cd modules/copy_fail_family && make" diff --git a/core/module.h b/core/module.h new file mode 100644 index 0000000..1c59e12 --- /dev/null +++ b/core/module.h @@ -0,0 +1,89 @@ +/* + * IAMROOT — core module interface + * + * Every CVE module exports one or more `struct iamroot_module` entries + * via a registry function. The top-level dispatcher (iamroot.c) walks + * the global registry to implement --scan, --exploit, --mitigate, etc. + * + * This is intentionally a small interface. Modules carry the + * complexity; the dispatcher just routes. + */ + +#ifndef IAMROOT_MODULE_H +#define IAMROOT_MODULE_H + +#include +#include + +/* Standard result codes returned by detect()/exploit()/mitigate(). + * + * These map to top-level exit codes when iamroot is invoked with a + * single-module operation: + * + * IAMROOT_OK exit 0 detect: not vulnerable / clean + * IAMROOT_VULNERABLE exit 2 detect: confirmed vulnerable + * IAMROOT_PRECOND_FAIL exit 4 detect: preconditions missing + * IAMROOT_TEST_ERROR exit 1 detect/exploit: error + * IAMROOT_EXPLOIT_OK exit 5 exploit: succeeded (root achieved) + * IAMROOT_EXPLOIT_FAIL exit 3 exploit: attempted but did not land + * + * Implementation note: copy_fail_family's df_result_t shares these + * numeric values intentionally so the family code can return its + * existing constants without translation. + */ +typedef enum { + IAMROOT_OK = 0, + IAMROOT_TEST_ERROR = 1, + IAMROOT_VULNERABLE = 2, + IAMROOT_EXPLOIT_FAIL = 3, + IAMROOT_PRECOND_FAIL = 4, + IAMROOT_EXPLOIT_OK = 5, +} iamroot_result_t; + +/* Per-invocation context passed to module callbacks. Lightweight for + * now; will grow as modules need shared state (host fingerprint, + * leaked kbase, etc.). */ +struct iamroot_ctx { + bool no_color; /* --no-color */ + bool json; /* --json (machine-readable output) */ + bool active_probe; /* --active (do invasive probes in detect) */ + bool no_shell; /* --no-shell (exploit prep but don't pop) */ + bool authorized; /* user typed --i-know on exploit */ +}; + +struct iamroot_module { + /* Short id used on the command line: `iamroot --exploit copy_fail`. */ + const char *name; + + /* CVE identifier (or "VARIANT" if no CVE assigned). */ + const char *cve; + + /* One-line human description. */ + const char *summary; + + /* Family this module belongs to (e.g. "copy_fail_family"). Modules + * with shared infrastructure live in the same family. */ + const char *family; + + /* Affected kernel range, prose. Machine-readable range goes in + * the module's kernel-range.json (consumed by CI). */ + const char *kernel_range; + + /* Probe the host. Should be side-effect-free unless ctx->active_probe + * is true. Return IAMROOT_VULNERABLE if confirmed, + * IAMROOT_PRECOND_FAIL if not applicable here, IAMROOT_OK if patched + * or otherwise immune, IAMROOT_TEST_ERROR on probe error. */ + iamroot_result_t (*detect)(const struct iamroot_ctx *ctx); + + /* Run the exploit. Caller has already passed the --i-know gate. */ + iamroot_result_t (*exploit)(const struct iamroot_ctx *ctx); + + /* Apply a temporary mitigation. NULL if none offered. */ + iamroot_result_t (*mitigate)(const struct iamroot_ctx *ctx); + + /* Undo --exploit (e.g. evict from page cache) or --mitigate side + * effects. NULL if no cleanup applies. */ + iamroot_result_t (*cleanup)(const struct iamroot_ctx *ctx); +}; + +#endif /* IAMROOT_MODULE_H */ diff --git a/core/registry.c b/core/registry.c new file mode 100644 index 0000000..891cb19 --- /dev/null +++ b/core/registry.c @@ -0,0 +1,60 @@ +/* + * IAMROOT — module registry implementation + * + * Simple flat array. Resized in chunks of 16. We never expect more + * than a few dozen modules, so this is fine. + */ + +#include "registry.h" + +#include +#include +#include +#include + +#define REGISTRY_CHUNK 16 + +static const struct iamroot_module **g_modules = NULL; +static size_t g_count = 0; +static size_t g_cap = 0; + +void iamroot_register(const struct iamroot_module *m) +{ + if (m == NULL || m->name == NULL) { + fprintf(stderr, "[!] iamroot_register: NULL module or unnamed module\n"); + return; + } + if (g_count == g_cap) { + size_t new_cap = g_cap + REGISTRY_CHUNK; + const struct iamroot_module **n = + realloc((void *)g_modules, new_cap * sizeof(*g_modules)); + if (n == NULL) { + fprintf(stderr, "[!] iamroot_register: OOM\n"); + return; + } + g_modules = n; + g_cap = new_cap; + } + g_modules[g_count++] = m; +} + +size_t iamroot_module_count(void) +{ + return g_count; +} + +const struct iamroot_module *iamroot_module_at(size_t i) +{ + if (i >= g_count) return NULL; + return g_modules[i]; +} + +const struct iamroot_module *iamroot_module_find(const char *name) +{ + if (name == NULL) return NULL; + for (size_t i = 0; i < g_count; i++) { + if (strcmp(g_modules[i]->name, name) == 0) + return g_modules[i]; + } + return NULL; +} diff --git a/core/registry.h b/core/registry.h new file mode 100644 index 0000000..3739782 --- /dev/null +++ b/core/registry.h @@ -0,0 +1,25 @@ +/* + * IAMROOT — module registry + * + * Global list of registered modules. Each family contributes via + * register__modules() called from iamroot main() at startup. + */ + +#ifndef IAMROOT_REGISTRY_H +#define IAMROOT_REGISTRY_H + +#include "module.h" + +void iamroot_register(const struct iamroot_module *m); + +size_t iamroot_module_count(void); +const struct iamroot_module *iamroot_module_at(size_t i); + +/* Find a module by name. Returns NULL if not found. */ +const struct iamroot_module *iamroot_module_find(const char *name); + +/* Each module family declares one of these in its public header. The + * top-level iamroot main() calls them in order at startup. */ +void iamroot_register_copy_fail_family(void); + +#endif /* IAMROOT_REGISTRY_H */ diff --git a/iamroot.c b/iamroot.c new file mode 100644 index 0000000..6d3b8d4 --- /dev/null +++ b/iamroot.c @@ -0,0 +1,235 @@ +/* + * IAMROOT — top-level dispatcher + * + * Usage: + * iamroot --scan # run every module's detect() + * iamroot --scan --json # machine-readable output + * iamroot --scan --active # invasive probes (still no /etc/passwd writes) + * iamroot --list # list registered modules + * iamroot --exploit --i-know # run a named module's exploit + * iamroot --mitigate # apply a temporary mitigation + * iamroot --cleanup # undo --exploit or --mitigate side effects + * + * Phase 1 scope: thin dispatcher over the copy_fail_family bridge. + * Future phases add: --detect-rules export, multi-family registry, + * fingerprint pre-pass, etc. + */ + +#include "core/module.h" +#include "core/registry.h" + +#include +#include +#include +#include +#include +#include + +#define IAMROOT_VERSION "0.1.0-phase1" + +static const char BANNER[] = +"\n" +" ██╗ █████╗ ███╗ ███╗██████╗ ██████╗ ██████╗ ████████╗\n" +" ██║██╔══██╗████╗ ████║██╔══██╗██╔═══██╗██╔═══██╗╚══██╔══╝\n" +" ██║███████║██╔████╔██║██████╔╝██║ ██║██║ ██║ ██║ \n" +" ██║██╔══██║██║╚██╔╝██║██╔══██╗██║ ██║██║ ██║ ██║ \n" +" ██║██║ ██║██║ ╚═╝ ██║██║ ██║╚██████╔╝╚██████╔╝ ██║ \n" +" ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ \n" +" Curated Linux kernel LPE corpus — v" IAMROOT_VERSION "\n" +" AUTHORIZED TESTING ONLY — see docs/ETHICS.md\n"; + +static void usage(const char *prog) +{ + fprintf(stderr, +"Usage: %s [MODE] [OPTIONS]\n" +"\n" +"Modes (default: --scan):\n" +" --scan run every module's detect() across the host\n" +" --list list registered modules and exit\n" +" --exploit run named module's exploit (REQUIRES --i-know)\n" +" --mitigate apply named module's mitigation\n" +" --cleanup undo named module's exploit/mitigate side effects\n" +" --version print version\n" +" --help this message\n" +"\n" +"Options:\n" +" --i-know authorization gate for --exploit modes\n" +" --active in --scan, do invasive sentinel probes (no /etc/passwd writes)\n" +" --no-shell in --exploit modes, prepare but don't drop to shell\n" +" --json machine-readable output (for SIEM/CI)\n" +" --no-color disable ANSI color codes\n" +"\n" +"Exit codes:\n" +" 0 not vulnerable / OK 2 vulnerable 5 exploit succeeded\n" +" 1 test error 3 exploit failed 4 preconditions missing\n", + prog); +} + +enum mode { + MODE_SCAN, + MODE_LIST, + MODE_EXPLOIT, + MODE_MITIGATE, + MODE_CLEANUP, + MODE_HELP, + MODE_VERSION, +}; + +static const char *result_str(iamroot_result_t r) +{ + switch (r) { + case IAMROOT_OK: return "OK"; + case IAMROOT_TEST_ERROR: return "ERROR"; + case IAMROOT_VULNERABLE: return "VULNERABLE"; + case IAMROOT_EXPLOIT_FAIL: return "EXPLOIT_FAIL"; + case IAMROOT_PRECOND_FAIL: return "PRECOND_FAIL"; + case IAMROOT_EXPLOIT_OK: return "EXPLOIT_OK"; + } + return "?"; +} + +static int cmd_list(void) +{ + size_t n = iamroot_module_count(); + fprintf(stdout, "%-20s %-18s %-25s %s\n", + "NAME", "CVE", "FAMILY", "SUMMARY"); + fprintf(stdout, "%-20s %-18s %-25s %s\n", + "----", "---", "------", "-------"); + for (size_t i = 0; i < n; i++) { + const struct iamroot_module *m = iamroot_module_at(i); + fprintf(stdout, "%-20s %-18s %-25s %s\n", + m->name, m->cve, m->family, m->summary); + } + return 0; +} + +static int cmd_scan(const struct iamroot_ctx *ctx) +{ + int worst = 0; + size_t n = iamroot_module_count(); + if (!ctx->json) { + fprintf(stderr, "[*] iamroot scan: %zu module(s) registered\n", n); + } else { + fprintf(stdout, "{\"version\":\"%s\",\"modules\":[", IAMROOT_VERSION); + } + for (size_t i = 0; i < n; i++) { + const struct iamroot_module *m = iamroot_module_at(i); + if (m->detect == NULL) continue; + iamroot_result_t r = m->detect(ctx); + if (ctx->json) { + fprintf(stdout, "%s{\"name\":\"%s\",\"cve\":\"%s\",\"result\":\"%s\"}", + (i == 0 ? "" : ","), m->name, m->cve, result_str(r)); + } else { + fprintf(stdout, "[%s] %-20s %-18s %s\n", + result_str(r), m->name, m->cve, m->summary); + } + /* track worst (highest) result code as overall exit */ + if ((int)r > worst) worst = (int)r; + } + if (ctx->json) { + fprintf(stdout, "]}\n"); + } + return worst; +} + +static int cmd_one(const struct iamroot_module *m, const char *op, + const struct iamroot_ctx *ctx) +{ + iamroot_result_t (*fn)(const struct iamroot_ctx *) = NULL; + if (strcmp(op, "exploit") == 0) fn = m->exploit; + else if (strcmp(op, "mitigate") == 0) fn = m->mitigate; + else if (strcmp(op, "cleanup") == 0) fn = m->cleanup; + + if (fn == NULL) { + fprintf(stderr, "[-] module '%s' has no %s operation\n", m->name, op); + return 1; + } + iamroot_result_t r = fn(ctx); + fprintf(stderr, "[*] %s --%s result: %s\n", m->name, op, result_str(r)); + return (int)r; +} + +int main(int argc, char **argv) +{ + /* Bring up the module registry. As new families land, add their + * register_* call here. */ + iamroot_register_copy_fail_family(); + + enum mode mode = MODE_SCAN; + struct iamroot_ctx ctx = {0}; + const char *target = NULL; + int i_know = 0; + + static struct option longopts[] = { + {"scan", no_argument, 0, 'S'}, + {"list", no_argument, 0, 'L'}, + {"exploit", required_argument, 0, 'E'}, + {"mitigate", required_argument, 0, 'M'}, + {"cleanup", required_argument, 0, 'C'}, + {"i-know", no_argument, 0, 1 }, + {"active", no_argument, 0, 2 }, + {"no-shell", no_argument, 0, 3 }, + {"json", no_argument, 0, 4 }, + {"no-color", no_argument, 0, 5 }, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + int c, opt_idx; + while ((c = getopt_long(argc, argv, "SLE:M:C:Vh", longopts, &opt_idx)) != -1) { + switch (c) { + case 'S': mode = MODE_SCAN; break; + case 'L': mode = MODE_LIST; break; + case 'E': mode = MODE_EXPLOIT; target = optarg; break; + case 'M': mode = MODE_MITIGATE; target = optarg; break; + case 'C': mode = MODE_CLEANUP; target = optarg; break; + case 1 : i_know = 1; ctx.authorized = true; break; + case 2 : ctx.active_probe = true; break; + case 3 : ctx.no_shell = true; break; + case 4 : ctx.json = true; break; + case 5 : ctx.no_color = true; break; + case 'V': printf("iamroot %s\n", IAMROOT_VERSION); return 0; + case 'h': mode = MODE_HELP; break; + default: usage(argv[0]); return 1; + } + } + + if (mode == MODE_HELP) { + fputs(BANNER, stderr); + usage(argv[0]); + return 0; + } + + if (!ctx.json) fputs(BANNER, stderr); + + if (mode == MODE_SCAN) return cmd_scan(&ctx); + if (mode == MODE_LIST) return cmd_list(); + + /* --exploit / --mitigate / --cleanup all take a target */ + if (target == NULL) { + fprintf(stderr, "[-] mode requires a module name\n"); + return 1; + } + const struct iamroot_module *m = iamroot_module_find(target); + if (m == NULL) { + fprintf(stderr, "[-] no module '%s'. Try --list.\n", target); + return 1; + } + + if (mode == MODE_EXPLOIT) { + if (!i_know) { + fprintf(stderr, + "[-] --exploit requires --i-know. This will attempt to gain\n" + " root and corrupt /etc/passwd in the page cache.\n" + " Authorized testing only. See docs/ETHICS.md.\n"); + return 1; + } + return cmd_one(m, "exploit", &ctx); + } + if (mode == MODE_MITIGATE) return cmd_one(m, "mitigate", &ctx); + if (mode == MODE_CLEANUP) return cmd_one(m, "cleanup", &ctx); + + usage(argv[0]); + return 1; +} diff --git a/modules/copy_fail_family/iamroot_modules.c b/modules/copy_fail_family/iamroot_modules.c new file mode 100644 index 0000000..5a11e52 --- /dev/null +++ b/modules/copy_fail_family/iamroot_modules.c @@ -0,0 +1,176 @@ +/* + * copy_fail_family — IAMROOT module bridge layer + * + * Wraps the existing per-CVE detect/exploit functions (from the + * absorbed DIRTYFAIL codebase) as standard iamroot_module entries. + * + * The bridge functions translate between the family's df_result_t + * (defined in src/common.h) and iamroot_result_t (defined in + * core/module.h). Numeric values are identical by design so the + * translation is a direct cast. + * + * iamroot_ctx fields (no_color, json, active_probe, no_shell) are + * forwarded to the family's existing global flags before each + * callback. This preserves DIRTYFAIL's existing CLI semantics + * unchanged. + */ + +#include "iamroot_modules.h" +#include "../../core/registry.h" + +#include "src/common.h" +#include "src/copyfail.h" +#include "src/copyfail_gcm.h" +#include "src/dirtyfrag_esp.h" +#include "src/dirtyfrag_esp6.h" +#include "src/dirtyfrag_rxrpc.h" + +static void apply_ctx(const struct iamroot_ctx *ctx) +{ + dirtyfail_use_color = !ctx->no_color; + dirtyfail_active_probes = ctx->active_probe; + dirtyfail_json = ctx->json; + /* dirtyfail_no_revert is intentionally not driven from ctx — + * it's a debug knob; default stays off. */ +} + +/* ----- copy_fail (CVE-2026-31431) ----- */ + +static iamroot_result_t copy_fail_detect_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)copyfail_detect(); +} + +static iamroot_result_t copy_fail_exploit_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)copyfail_exploit(!ctx->no_shell); +} + +const struct iamroot_module copy_fail_module = { + .name = "copy_fail", + .cve = "CVE-2026-31431", + .summary = "algif_aead authencesn page-cache write → /etc/passwd UID flip", + .family = "copy_fail_family", + .kernel_range = "≤ 6.12.84, fixed mainline 2026-04-22", + .detect = copy_fail_detect_wrap, + .exploit = copy_fail_exploit_wrap, + .mitigate = NULL, + .cleanup = NULL, +}; + +/* ----- copy_fail_gcm (variant, no CVE) ----- */ + +static iamroot_result_t copy_fail_gcm_detect_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)copyfail_gcm_detect(); +} + +static iamroot_result_t copy_fail_gcm_exploit_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)copyfail_gcm_exploit(!ctx->no_shell); +} + +const struct iamroot_module copy_fail_gcm_module = { + .name = "copy_fail_gcm", + .cve = "VARIANT", + .summary = "rfc4106(gcm(aes)) single-byte page-cache write (Copy Fail sibling)", + .family = "copy_fail_family", + .kernel_range = "same as copy_fail; rfc4106(gcm(aes)) not in modprobe blacklist", + .detect = copy_fail_gcm_detect_wrap, + .exploit = copy_fail_gcm_exploit_wrap, + .mitigate = NULL, + .cleanup = NULL, +}; + +/* ----- dirty_frag_esp (CVE-2026-43284 v4) ----- */ + +static iamroot_result_t dirty_frag_esp_detect_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_esp_detect(); +} + +static iamroot_result_t dirty_frag_esp_exploit_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_esp_exploit(!ctx->no_shell); +} + +const struct iamroot_module dirty_frag_esp_module = { + .name = "dirty_frag_esp", + .cve = "CVE-2026-43284", + .summary = "IPv4 xfrm-ESP page-cache write (Dirty Frag v4)", + .family = "copy_fail_family", + .kernel_range = "same family as copy_fail; xfrm-ESP path", + .detect = dirty_frag_esp_detect_wrap, + .exploit = dirty_frag_esp_exploit_wrap, + .mitigate = NULL, + .cleanup = NULL, +}; + +/* ----- dirty_frag_esp6 (CVE-2026-43284 v6) ----- */ + +static iamroot_result_t dirty_frag_esp6_detect_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_esp6_detect(); +} + +static iamroot_result_t dirty_frag_esp6_exploit_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_esp6_exploit(!ctx->no_shell); +} + +const struct iamroot_module dirty_frag_esp6_module = { + .name = "dirty_frag_esp6", + .cve = "CVE-2026-43284", + .summary = "IPv6 xfrm-ESP page-cache write (Dirty Frag v6)", + .family = "copy_fail_family", + .kernel_range = "same family as copy_fail; xfrm-ESP6 path; V6 STORE shift auto-calibrated", + .detect = dirty_frag_esp6_detect_wrap, + .exploit = dirty_frag_esp6_exploit_wrap, + .mitigate = NULL, + .cleanup = NULL, +}; + +/* ----- dirty_frag_rxrpc (CVE-2026-43500) ----- */ + +static iamroot_result_t dirty_frag_rxrpc_detect_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_rxrpc_detect(); +} + +static iamroot_result_t dirty_frag_rxrpc_exploit_wrap(const struct iamroot_ctx *ctx) +{ + apply_ctx(ctx); + return (iamroot_result_t)dirtyfrag_rxrpc_exploit(!ctx->no_shell); +} + +const struct iamroot_module dirty_frag_rxrpc_module = { + .name = "dirty_frag_rxrpc", + .cve = "CVE-2026-43500", + .summary = "AF_RXRPC handshake forgery + page-cache write (Dirty Frag RxRPC)", + .family = "copy_fail_family", + .kernel_range = "kernels exposing AF_RXRPC + rxkad with fcrypt fallback", + .detect = dirty_frag_rxrpc_detect_wrap, + .exploit = dirty_frag_rxrpc_exploit_wrap, + .mitigate = NULL, + .cleanup = NULL, +}; + +/* ----- Family registration ----- */ + +void iamroot_register_copy_fail_family(void) +{ + iamroot_register(©_fail_module); + iamroot_register(©_fail_gcm_module); + iamroot_register(&dirty_frag_esp_module); + iamroot_register(&dirty_frag_esp6_module); + iamroot_register(&dirty_frag_rxrpc_module); +} diff --git a/modules/copy_fail_family/iamroot_modules.h b/modules/copy_fail_family/iamroot_modules.h new file mode 100644 index 0000000..93de48f --- /dev/null +++ b/modules/copy_fail_family/iamroot_modules.h @@ -0,0 +1,28 @@ +/* + * copy_fail_family — IAMROOT module registry hooks + * + * The family currently contains five iamroot_module entries: + * + * - copy_fail (CVE-2026-31431, algif_aead authencesn) + * - copy_fail_gcm (no CVE, rfc4106(gcm(aes)) variant) + * - dirty_frag_esp (CVE-2026-43284 v4) + * - dirty_frag_esp6 (CVE-2026-43284 v6) + * - dirty_frag_rxrpc (CVE-2026-43500) + * + * Defined in iamroot_modules.c, registered into the global registry + * by iamroot_register_copy_fail_family() (declared in + * core/registry.h). + */ + +#ifndef COPY_FAIL_FAMILY_IAMROOT_MODULES_H +#define COPY_FAIL_FAMILY_IAMROOT_MODULES_H + +#include "../../core/module.h" + +extern const struct iamroot_module copy_fail_module; +extern const struct iamroot_module copy_fail_gcm_module; +extern const struct iamroot_module dirty_frag_esp_module; +extern const struct iamroot_module dirty_frag_esp6_module; +extern const struct iamroot_module dirty_frag_rxrpc_module; + +#endif diff --git a/modules/copy_fail_family/apparmor_bypass.c b/modules/copy_fail_family/src/apparmor_bypass.c similarity index 100% rename from modules/copy_fail_family/apparmor_bypass.c rename to modules/copy_fail_family/src/apparmor_bypass.c diff --git a/modules/copy_fail_family/apparmor_bypass.h b/modules/copy_fail_family/src/apparmor_bypass.h similarity index 100% rename from modules/copy_fail_family/apparmor_bypass.h rename to modules/copy_fail_family/src/apparmor_bypass.h diff --git a/modules/copy_fail_family/backdoor.c b/modules/copy_fail_family/src/backdoor.c similarity index 100% rename from modules/copy_fail_family/backdoor.c rename to modules/copy_fail_family/src/backdoor.c diff --git a/modules/copy_fail_family/backdoor.h b/modules/copy_fail_family/src/backdoor.h similarity index 100% rename from modules/copy_fail_family/backdoor.h rename to modules/copy_fail_family/src/backdoor.h diff --git a/modules/copy_fail_family/common.c b/modules/copy_fail_family/src/common.c similarity index 100% rename from modules/copy_fail_family/common.c rename to modules/copy_fail_family/src/common.c diff --git a/modules/copy_fail_family/common.h b/modules/copy_fail_family/src/common.h similarity index 100% rename from modules/copy_fail_family/common.h rename to modules/copy_fail_family/src/common.h diff --git a/modules/copy_fail_family/copyfail.c b/modules/copy_fail_family/src/copyfail.c similarity index 100% rename from modules/copy_fail_family/copyfail.c rename to modules/copy_fail_family/src/copyfail.c diff --git a/modules/copy_fail_family/copyfail.h b/modules/copy_fail_family/src/copyfail.h similarity index 100% rename from modules/copy_fail_family/copyfail.h rename to modules/copy_fail_family/src/copyfail.h diff --git a/modules/copy_fail_family/copyfail_gcm.c b/modules/copy_fail_family/src/copyfail_gcm.c similarity index 100% rename from modules/copy_fail_family/copyfail_gcm.c rename to modules/copy_fail_family/src/copyfail_gcm.c diff --git a/modules/copy_fail_family/copyfail_gcm.h b/modules/copy_fail_family/src/copyfail_gcm.h similarity index 100% rename from modules/copy_fail_family/copyfail_gcm.h rename to modules/copy_fail_family/src/copyfail_gcm.h diff --git a/modules/copy_fail_family/dirtyfail.c b/modules/copy_fail_family/src/dirtyfail.c similarity index 100% rename from modules/copy_fail_family/dirtyfail.c rename to modules/copy_fail_family/src/dirtyfail.c diff --git a/modules/copy_fail_family/dirtyfrag_esp.c b/modules/copy_fail_family/src/dirtyfrag_esp.c similarity index 100% rename from modules/copy_fail_family/dirtyfrag_esp.c rename to modules/copy_fail_family/src/dirtyfrag_esp.c diff --git a/modules/copy_fail_family/dirtyfrag_esp.h b/modules/copy_fail_family/src/dirtyfrag_esp.h similarity index 100% rename from modules/copy_fail_family/dirtyfrag_esp.h rename to modules/copy_fail_family/src/dirtyfrag_esp.h diff --git a/modules/copy_fail_family/dirtyfrag_esp6.c b/modules/copy_fail_family/src/dirtyfrag_esp6.c similarity index 100% rename from modules/copy_fail_family/dirtyfrag_esp6.c rename to modules/copy_fail_family/src/dirtyfrag_esp6.c diff --git a/modules/copy_fail_family/dirtyfrag_esp6.h b/modules/copy_fail_family/src/dirtyfrag_esp6.h similarity index 100% rename from modules/copy_fail_family/dirtyfrag_esp6.h rename to modules/copy_fail_family/src/dirtyfrag_esp6.h diff --git a/modules/copy_fail_family/dirtyfrag_rxrpc.c b/modules/copy_fail_family/src/dirtyfrag_rxrpc.c similarity index 100% rename from modules/copy_fail_family/dirtyfrag_rxrpc.c rename to modules/copy_fail_family/src/dirtyfrag_rxrpc.c diff --git a/modules/copy_fail_family/dirtyfrag_rxrpc.h b/modules/copy_fail_family/src/dirtyfrag_rxrpc.h similarity index 100% rename from modules/copy_fail_family/dirtyfrag_rxrpc.h rename to modules/copy_fail_family/src/dirtyfrag_rxrpc.h diff --git a/modules/copy_fail_family/exploit_su.c b/modules/copy_fail_family/src/exploit_su.c similarity index 100% rename from modules/copy_fail_family/exploit_su.c rename to modules/copy_fail_family/src/exploit_su.c diff --git a/modules/copy_fail_family/exploit_su.h b/modules/copy_fail_family/src/exploit_su.h similarity index 100% rename from modules/copy_fail_family/exploit_su.h rename to modules/copy_fail_family/src/exploit_su.h diff --git a/modules/copy_fail_family/fcrypt.c b/modules/copy_fail_family/src/fcrypt.c similarity index 100% rename from modules/copy_fail_family/fcrypt.c rename to modules/copy_fail_family/src/fcrypt.c diff --git a/modules/copy_fail_family/fcrypt.h b/modules/copy_fail_family/src/fcrypt.h similarity index 100% rename from modules/copy_fail_family/fcrypt.h rename to modules/copy_fail_family/src/fcrypt.h diff --git a/modules/copy_fail_family/mitigate.c b/modules/copy_fail_family/src/mitigate.c similarity index 100% rename from modules/copy_fail_family/mitigate.c rename to modules/copy_fail_family/src/mitigate.c diff --git a/modules/copy_fail_family/mitigate.h b/modules/copy_fail_family/src/mitigate.h similarity index 100% rename from modules/copy_fail_family/mitigate.h rename to modules/copy_fail_family/src/mitigate.h