027fc1f9ddc2f9b667edde1a3ce24600033ee595
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2b1e96336e |
core/host: in_range helper + 13-module migration + 12 more tests (29 total)
Three coordinated changes that build on the host_kernel_at_least
landed in
|
||
|
|
36814f272d |
modules: migrate remaining 22 modules to ctx->host fingerprint
Completes the host-fingerprint refactor that started in
|
||
|
|
cdb8f5e8f9 |
all modules: wrap Linux-only code in #ifdef __linux__ — full macOS build works
Every kernel-LPE module that uses Linux-only headers (splice, posix_fadvise,
linux/netlink.h, sys/ptrace.h, etc.) now follows the same #ifdef __linux__
pattern the new modules already used: Linux body in the ifdef, stub
detect/exploit/cleanup returning SKELETONKEY_PRECOND_FAIL on non-Linux,
platform-neutral rule strings + module struct + register fn left outside.
14 modules wrapped:
dirty_pipe (already done above), af_packet, af_packet2,
cgroup_release_agent, cls_route4, dirty_cow, fuse_legacy,
netfilter_xtcompat, nf_tables, nft_fwd_dup, nft_payload,
overlayfs, overlayfs_setuid, ptrace_traceme.
Several modules previously had ad-hoc partial stubs (af_packet2 faked
SIOCSIFFLAGS/MAP_LOCKED, netfilter_xtcompat faked sysv-msg syscalls,
the nft_* modules had 3 partial __linux__ islands each, fuse_legacy /
nf_tables had inner-only ifdef blocks) — all replaced with the uniform
outer-wrap shape from dirty_pipe / dirtydecrypt / fragnesia / pack2theroot.
Where a module includes core/kernel_range.h, core/finisher.h, or
core/offsets.h, those are now inside the ifdef block as well — silences
clangd's "unused-includes" LSP warning on macOS while keeping them
present for the real Linux build.
No exploit logic, constant, struct, shellcode byte, or rule string was
modified — only include placement and ifdef markers.
Build verification:
macOS (local): make clean && make → Mach-O x86_64, 31 modules
registered, --scan reports each Linux-only module as
"Linux-only module — not applicable here".
Linux (docker gcc:latest + libglib2.0-dev): make clean && make →
ELF 64-bit, 31 modules. Exploit code paths unchanged.
|
||
|
|
9593d90385 |
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.
|
||
|
|
9d88b475c1 |
v0.3.1: --dump-offsets tool + NOTICE.md per module
The README has been claiming "each module credits the original CVE
reporter and PoC author in its NOTICE.md" since v0.1.0, but only
copy_fail_family actually shipped one. Fixed.
modules/<name>/NOTICE.md (×19 new + 1 existing): per-module
research credit covering CVE ID, discoverer, original advisory
URL where public, upstream fix commit, IAMROOT's role.
iamroot.c: new --dump-offsets subcommand. Resolves kernel offsets
via the existing core/offsets.c four-source chain (env →
/proc/kallsyms → /boot/System.map → embedded table), then emits
a ready-to-paste C struct entry for kernel_table[]. Run once
as root on a target kernel build; upstream via PR. Eliminates
fabricating offsets — every shipped entry traces back to a
`iamroot --dump-offsets` invocation on a real kernel.
docs/OFFSETS.md: documents the --dump-offsets workflow.
CVES.md: notes the NOTICE.md convention + offset dump tool.
iamroot.c: bump IAMROOT_VERSION 0.3.0 → 0.3.1.
|
||
|
|
c1d1910a90 |
modules: wire --full-chain root-pop into all 7 🟡 PRIMITIVE modules
Each module now exposes an opt-in full-chain root-pop via --full-chain:
default --exploit behavior is unchanged (primitive-only, returns
EXPLOIT_FAIL). With --full-chain, after primitive lands, modules call
iamroot_finisher_modprobe_path() via a module-specific arb_write_fn
that re-uses the same trigger + slab groom to write a userspace
payload path into modprobe_path[], then exec a setuid bash dropped
by the kernel-invoked modprobe.
netfilter_xtcompat (+239): msg_msg m_list_next stride-seed FALLBACK
af_packet (+316): sk_buff data-pointer stride-seed FALLBACK
af_packet2 (+156): tp_reserve underflow + skb spray, LAST RESORT
nf_tables (+275): forged pipapo_elem with kaddr value-ptr
(Notselwyn offset 0x10), FALLBACK
cls_route4 (+251): msg_msg refill of UAF'd filter, FALLBACK
fuse_legacy (+291): m_ts overflow + MSG_COPY sanity gate,
FALLBACK (one of two modules with a real
post-write sanity check)
stackrot (+233): race-driver budget extended 3s → 30s when
--full-chain; honest <1% race-win/run
All seven honor verified-vs-claimed: arb_write_fn returns 0 for
"trigger structurally fired"; the shared finisher's setuid-bash
sentinel poll is the empirical arbiter. EXPLOIT_OK only when the
sentinel materializes within 3s of the modprobe_path trigger.
Build clean on Debian 6.12.86 (kctf-mgr); all 7 modules refuse
cleanly on both default and --full-chain paths via the existing
patched-kernel detect gate (short-circuits before the new branch).
|
||
|
|
498bb36404 |
modules: port 5 detect-only modules to trigger+groom (Option B)
Converts the 5 remaining detect-only network/fs LPE modules to fire
the actual kernel primitive on a vulnerable host, with honest
EXPLOIT_FAIL return values since none ship the per-kernel cred-overwrite
finisher.
af_packet (CVE-2017-7308): +444 LoC — TPACKET_V3 int-overflow
+ skb spray + best-effort cred race
af_packet2 (CVE-2020-14386): +446 LoC — tp_reserve underflow
+ sendmmsg skb spray
cls_route4 (CVE-2022-2588): +410 LoC — route4 dangling-filter UAF
+ msg_msg 1k spray + classify drive
fuse_legacy (CVE-2022-0185): +420 LoC — fsconfig 4k OOB write
+ msg_msg cross-cache groom
nf_tables (CVE-2024-1086): +613 LoC — hand-rolled nfnetlink batch
builder + NFT_GOTO/DROP double-free
+ msg_msg groom skeleton
All five share:
- userns+netns reach (unshare(CLONE_NEWUSER|CLONE_NEWNET))
- Detect-refuse-on-patched re-call from exploit()
- geteuid()==0 short-circuit
- Honest EXPLOIT_FAIL with continuation roadmap comments
- macOS dev-build stubs via #ifdef __linux__ where needed
Build verified clean on Debian 6.12.86 (kctf-mgr). All five refuse on
the patched kernel.
|
||
|
|
a4b7238e4a |
Phase 7: nf_tables CVE-2024-1086 + active probe for dirty_pipe
dirty_pipe detect: active sentinel probe (Phase 1.5-ish improvement)
- New dirty_pipe_active_probe(): creates a /tmp probe file with known
sentinel bytes, fires the Dirty Pipe primitive against it, re-reads
via the page cache, returns true if the poisoning landed.
- detect() gated on ctx->active_probe: --scan does version-only check
(fast, no side effects); --scan --active fires the empirical probe
and overrides version inference with the empirical verdict. Catches
silent distro backports that don't bump uname() version.
- Three verdicts now distinguishable:
(a) version says patched, no active probe → 'patched (version-only)'
(b) version says vulnerable, --active fires + probe lands → CONFIRMED
(c) version says vulnerable, --active fires + probe blocked → 'likely
patched via distro backport'
- Probe is safe: only /tmp, no /etc/passwd.
nf_tables CVE-2024-1086 (detect-only, new module):
- Famous Notselwyn UAF in nft_verdict_init. Affects 5.14 ≤ K, fixed
mainline 6.8 with backports landing in 5.4.269 / 5.10.210 / 5.15.149
/ 6.1.74 / 6.6.13 / 6.7.2.
- detect() checks: kernel version range, AND unprivileged user_ns clone
availability (the exploit's reachability gate — kernel-vulnerable
but userns-locked-down hosts report PRECOND_FAIL, signalling that
the kernel still needs patching but unprivileged path is closed).
- Ships auditd + sigma detection rules: unshare(CLONE_NEWUSER) chained
with setresuid(0,0,0) on a previously-non-root process is the
exploit's canonical telltale.
- Full Notselwyn-style exploit (cross-cache UAF → arbitrary R/W → cred
overwrite or modprobe_path hijack) is the next commit.
9 modules total now. CVES.md and ROADMAP.md updated.
|