a9c8f7d8c669ba54c9639ed450f1564a933fcaad
7 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
|
||
|
|
4f30d00a1c |
core/host: shared host fingerprint refactor
Adds core/host.{h,c} — a single struct skeletonkey_host populated once
at startup and handed to every module callback via ctx->host. Replaces
the per-detect uname / /etc/os-release / sysctl / userns-fork-probe
calls scattered across the corpus with O(1) cached lookups, and gives
the dispatcher one consistent view of the host.
What's in the fingerprint:
- Identity: kernel_version (parsed from uname.release), arch (machine),
nodename, distro_id / distro_version_id / distro_pretty (parsed once
from /etc/os-release).
- Process state: euid, real_uid (defeats userns illusion via
/proc/self/uid_map), egid, username, is_root, is_ssh_session.
- Platform family: is_linux, is_debian_family, is_rpm_family,
is_arch_family, is_suse_family (file-existence checks once).
- Capability gates (Linux): unprivileged_userns_allowed (live
fork+unshare probe), apparmor_restrict_userns,
unprivileged_bpf_disabled, kpti_enabled, kernel_lockdown_active,
selinux_enforcing, yama_ptrace_restricted.
- System services: has_systemd, has_dbus_system.
Wiring:
- core/module.h forward-declares struct skeletonkey_host and adds the
pointer to skeletonkey_ctx. Modules opt-in by including
../../core/host.h.
- core/host.c is fully POD (no heap pointers) — uses a single file-
static instance, returns a stable pointer on every call. Lazily
populated on first skeletonkey_host_get().
- skeletonkey.c calls skeletonkey_host_get() at main() entry, stores
in ctx.host before any register_*() runs.
- cmd_auto's bespoke distro-fingerprint code (was an inline
read_os_release helper) is replaced with skeletonkey_host_print_banner(),
which emits a two-line banner of identity + capability gates.
Migrations:
- dirtydecrypt: kernel_version_current() -> ctx->host->kernel.
- fragnesia: removed local fg_userns_allowed() fork-probe in favour of
ctx->host->unprivileged_userns_allowed (no per-scan fork). Also
pulls kernel from ctx->host. The PRECOND_FAIL message now notes
whether AppArmor restriction is on.
- pack2theroot: access('/etc/debian_version') -> ctx->host->is_debian_family;
also short-circuits when ctx->host->has_dbus_system is false (saves
the GLib g_bus_get_sync attempt on systems without system D-Bus).
- overlayfs: replaced the inline is_ubuntu() /etc/os-release parser
with ctx->host->distro_id comparison. Local helper preserved for
symmetry / standalone builds.
Documentation: docs/ARCHITECTURE.md gains a 'Host fingerprint'
section describing the struct, the opt-in include pattern, and
example detect() usage. ROADMAP --auto accuracy log notes the
landing and flags remaining modules as an incremental follow-up.
Build verification:
- macOS (local): make clean && make -> Mach-O x86_64, 31 modules,
banner prints with distro=?/? (no /etc/os-release).
- Linux (docker gcc:latest + libglib2.0-dev): make clean && make ->
ELF 64-bit, 31 modules. Banner prints with kernel + distro=debian/13
+ 7 capability gates. dirtydecrypt correctly says 'predates the
rxgk code added in 7.0'; fragnesia PRECOND_FAILs with
'(host fingerprint)' annotation; pack2theroot PRECOND_FAILs on
no-DBus; overlayfs reports 'not Ubuntu (distro=debian)'.
|
||
|
|
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.
|
||
|
|
e2fcc6a9e0 |
Phase 7: overlayfs CVE-2021-3493 — port FULL exploit (vsh-style)
Convert overlayfs from 🔵 → 🟢: full vsh-style userns + overlayfs + file-capability injection exploit. Sequence: 1. mkdtemp workdir; gcc-compile a minimal payload that setresuid(0,0,0) + execle(/bin/sh, -p) 2. fork child; child unshares(CLONE_NEWUSER | CLONE_NEWNS), writes /proc/self/{setgroups,uid_map,gid_map} mapping outer uid to userns-root 3. child mounts overlayfs with lower/upper/work layout 4. child copies payload binary into merged/payload — this writes to host's upper/payload via the overlay 5. child writes security.capability xattr with VFS_CAP_REVISION_2 blob granting cap_setuid+ep on merged/payload — the BUG persists this xattr to the host fs entry 6. child exits; parent verifies xattr via getxattr on upper/payload 7. parent execve's upper/payload from outside userns → has cap_setuid effective → setuid(0) → /bin/sh -p with uid=0 - libcap-less setcap: build VFS_CAP_REVISION_2 blob in-place (cap_setuid bit 7, cap_setgid bit 6, effective flag set in magic_etc), write via setxattr(security.capability). - which_gcc() fallback to /usr/bin/cc, /bin/gcc, etc.; tries -static first, falls back to dynamic link if static unavailable. - Re-runs detect() to refuse on patched / non-Ubuntu hosts. - Cleanup on failure: rmdir/unlink the workdir tree. - Removed unused write_uid_gid_map() helper (logic now inline in child since we self-write the maps post-unshare). Verified end-to-end on Debian kctf-mgr: iamroot --exploit overlayfs --i-know → 'not Ubuntu — bug is Ubuntu-specific' → 'refusing'. Correct. Path buffers oversized vs. mkdtemp template to silence GCC -Wformat-truncation noise. CVES.md: overlayfs 🔵 → 🟢. |
||
|
|
3eeee01f06 |
Phase 7: overlayfs CVE-2021-3493 module (Ubuntu userns LPE) — detect-only
10th module. Ubuntu-specific userns + overlayfs LPE that injects file
capabilities cross-namespace.
- modules/overlayfs_cve_2021_3493/iamroot_modules.{c,h}:
- is_ubuntu() — parses /etc/os-release for ID=ubuntu or
ID_LIKE=ubuntu. Non-Ubuntu hosts get IAMROOT_OK immediately (the
bug is specific to Ubuntu's modified overlayfs).
- unprivileged_userns_clone gate — sysctl=0 → PRECOND_FAIL
- Active probe (--active): forks a child that enters userns +
mountns and attempts the overlayfs mount inside /tmp. Mount
success on Ubuntu = VULNERABLE. Mount denied = patched / AppArmor
block. Child-isolated so parent's namespace state is untouched.
- Version fallback: kernel < 5.13 = vulnerable-by-inference for
Ubuntu kernels; recommend --active for confirmation.
- Exploit: detect-only stub. Reference vsh's exploit-cve-2021-3493
for full version (mount overlayfs in userns, drop binary with
cap_setuid+ep into upper layer, re-exec outside ns).
- Embedded auditd rules: mount(overlay) syscall + security.capability
xattr writes (the exploit's two-step footprint).
Verified end-to-end on kctf-mgr (Debian):
iamroot --scan → 'not Ubuntu — bug is Ubuntu-specific' → IAMROOT_OK
Module count: 10. Active-probe pattern now applies to dirty_pipe,
entrybleed, and overlayfs (and copy_fail_family via existing
dirtyfail_active_probes global). Detect quality across the corpus
materially improved this session.
|