From 39ce4dff09089bf7869f1d72a7f4f759cca75dc4 Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Sat, 23 May 2026 10:45:38 -0400 Subject: [PATCH] =?UTF-8?q?modules:=20per-module=20OPSEC=20notes=20?= =?UTF-8?q?=E2=80=94=20telemetry=20footprint=20per=20exploit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds .opsec_notes to every module's struct skeletonkey_module (31 entries across 26 module files). One paragraph per exploit describing the runtime footprint a defender/SOC would see: - file artifacts created/modified (exact paths from source) - syscall observables (the unshare / socket / setsockopt / splice / msgsnd patterns the embedded detection rules look for) - dmesg signatures (silent on success vs KASAN oops on miss) - network activity (loopback-only vs none) - persistence side-effects (/etc/passwd modification, dropped setuid binaries, backdoors) - cleanup behaviour (callback present? what it restores?) Each note is grounded in the module's source code + its existing auditd/sigma/yara/falco detection rules — the OPSEC notes are literally the inverse of those rules (the rules describe what to look for; the notes describe what the exploit triggers). Three intelligence agents researched the modules in parallel, reading source + MODULE.md, then their proposals were embedded verbatim via tools/inject_opsec.py (one-shot script, not retained). Where surfaced: - --module-info : '--- opsec notes ---' section between detect-rules summary and the embedded auditd/sigma rule bodies. - --module-info / --scan --json: 'opsec_notes' top-level string. Audience uses: - Red team: see what footprint each exploit leaves so they pick chains that match the host's telemetry posture. - Blue team: the notes mirror the existing detection rules from the attacker side — easy diff to find gaps in their SIEM coverage. - Researchers: per-exploit footprint catalog for technique analysis. copy_fail_family gets one shared note across all 5 register entries (copy_fail, copy_fail_gcm, dirty_frag_esp, dirty_frag_esp6, dirty_frag_rxrpc) since they share exploit infrastructure. Verification: - macOS local: clean build, --module-info nf_tables shows full opsec section + CWE + ATT&CK + KEV row from previous commit. - Linux (docker gcc:latest): 33 + 54 = 87 passes, 0 fails. Next: --explain mode (uses these notes + the triage metadata to render a single 'why is this verdict, what would patch fix it, and what would the SOC see' page per module). --- modules/af_packet2_cve_2020_14386/skeletonkey_modules.c | 1 + modules/af_packet_cve_2017_7308/skeletonkey_modules.c | 1 + modules/af_unix_gc_cve_2023_4622/skeletonkey_modules.c | 1 + .../cgroup_release_agent_cve_2022_0492/skeletonkey_modules.c | 1 + modules/cls_route4_cve_2022_2588/skeletonkey_modules.c | 1 + modules/copy_fail_family/skeletonkey_modules.c | 5 +++++ modules/dirty_cow_cve_2016_5195/skeletonkey_modules.c | 1 + modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c | 1 + modules/dirtydecrypt_cve_2026_31635/skeletonkey_modules.c | 1 + modules/entrybleed_cve_2023_0458/skeletonkey_modules.c | 1 + modules/fragnesia_cve_2026_46300/skeletonkey_modules.c | 1 + modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c | 1 + .../netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c | 1 + modules/nf_tables_cve_2024_1086/skeletonkey_modules.c | 1 + modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c | 1 + modules/nft_payload_cve_2023_0179/skeletonkey_modules.c | 1 + modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c | 1 + modules/overlayfs_cve_2021_3493/skeletonkey_modules.c | 1 + modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c | 1 + modules/pack2theroot_cve_2026_41651/skeletonkey_modules.c | 1 + modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c | 1 + modules/pwnkit_cve_2021_4034/skeletonkey_modules.c | 1 + modules/sequoia_cve_2021_33909/skeletonkey_modules.c | 1 + modules/stackrot_cve_2023_3269/skeletonkey_modules.c | 1 + modules/sudo_samedit_cve_2021_3156/skeletonkey_modules.c | 1 + modules/sudoedit_editor_cve_2023_22809/skeletonkey_modules.c | 1 + modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c | 1 + 27 files changed, 31 insertions(+) diff --git a/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c b/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c index 3378414..38abd43 100644 --- a/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c +++ b/modules/af_packet2_cve_2020_14386/skeletonkey_modules.c @@ -683,6 +683,7 @@ const struct skeletonkey_module af_packet2_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + TPACKET_V2 ring on AF_PACKET; crafts nested-VLAN ETH_P_8021AD frames with 0x88A8/0x8100 TPIDs to trigger tpacket_rcv underflow; fires 256 frames + 64 sendmmsg via AF_UNIX socketpair spray. Tag 'skeletonkey-afp-fc-' visible in KASAN splats. Audit-visible via socket(AF_PACKET) + sendmsg/sendto from userns. No persistent artifacts; kernel cleans up on child exit.", }; void skeletonkey_register_af_packet2(void) diff --git a/modules/af_packet_cve_2017_7308/skeletonkey_modules.c b/modules/af_packet_cve_2017_7308/skeletonkey_modules.c index 4b3dd31..3a318f1 100644 --- a/modules/af_packet_cve_2017_7308/skeletonkey_modules.c +++ b/modules/af_packet_cve_2017_7308/skeletonkey_modules.c @@ -905,6 +905,7 @@ const struct skeletonkey_module af_packet_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Creates AF_PACKET socket and TPACKET_V3 ring inside unshare(CLONE_NEWUSER|CLONE_NEWNET); triggers integer overflow with crafted tp_block_size/tp_block_nr and sprays ~200 loopback frames. Audit-visible via socket(AF_PACKET) (a0=17) + sendmmsg from a userns process; KASAN tag 'iamroot-afp-tag' may appear in dmesg if enabled. No persistent files. No cleanup callback - kernel state unwinds on child exit.", }; void skeletonkey_register_af_packet(void) diff --git a/modules/af_unix_gc_cve_2023_4622/skeletonkey_modules.c b/modules/af_unix_gc_cve_2023_4622/skeletonkey_modules.c index 80e7cb9..4136fa6 100644 --- a/modules/af_unix_gc_cve_2023_4622/skeletonkey_modules.c +++ b/modules/af_unix_gc_cve_2023_4622/skeletonkey_modules.c @@ -847,6 +847,7 @@ const struct skeletonkey_module af_unix_gc_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Two-threaded race: Thread A creates socketpair(AF_UNIX) with SCM_RIGHTS cycle then close; Thread B drives independent SCM_RIGHTS traffic on a held pair. ~5s budget (30s with --full-chain). msg_msg kmalloc-512 spray tagged 'SKELETONKEYU'. Writes /tmp/skeletonkey-af_unix_gc.log with empirical stats. Audit-visible via socketpair(AF_UNIX) + sendmsg(SCM_RIGHTS) + msgsnd triple. Dmesg may show UAF KASAN if kernel vulnerable. Cleanup callback unlinks the log.", }; void skeletonkey_register_af_unix_gc(void) diff --git a/modules/cgroup_release_agent_cve_2022_0492/skeletonkey_modules.c b/modules/cgroup_release_agent_cve_2022_0492/skeletonkey_modules.c index c6a7412..9537c69 100644 --- a/modules/cgroup_release_agent_cve_2022_0492/skeletonkey_modules.c +++ b/modules/cgroup_release_agent_cve_2022_0492/skeletonkey_modules.c @@ -373,6 +373,7 @@ const struct skeletonkey_module cgroup_release_agent_module = { .detect_sigma = cgroup_ra_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNS), mount cgroup v1 at /tmp/skeletonkey-cgroup-mnt, write payload path to release_agent file at cgroup root, echo 1 to notify_on_release in subdir, add PID to cgroup.procs and exit. Payload at /tmp/skeletonkey-cgroup-payload.sh runs as init-namespace root when cgroup empties, dropping setuid /tmp/skeletonkey-cgroup-sh. Audit-visible via unshare + mount(cgroup) + open/write of release_agent. Cleanup callback removes /tmp/skeletonkey-cgroup-* and umounts.", }; void skeletonkey_register_cgroup_release_agent(void) diff --git a/modules/cls_route4_cve_2022_2588/skeletonkey_modules.c b/modules/cls_route4_cve_2022_2588/skeletonkey_modules.c index d437220..25f2862 100644 --- a/modules/cls_route4_cve_2022_2588/skeletonkey_modules.c +++ b/modules/cls_route4_cve_2022_2588/skeletonkey_modules.c @@ -840,6 +840,7 @@ const struct skeletonkey_module cls_route4_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET); ip link/addr/route to make a dummy interface, htb qdisc + class + route4 filter with handle 0, delete filter (leaves dangling tcf_proto pointer), msg_msg spray kmalloc-1k tagged 'SKELETONKEY4', UDP sendto to trigger classify(). Writes /tmp/skeletonkey-cls_route4.log. Audit-visible via unshare + sendto(AF_INET) + msgsnd. Cleanup callback removes /tmp log + dummy interface.", }; void skeletonkey_register_cls_route4(void) diff --git a/modules/copy_fail_family/skeletonkey_modules.c b/modules/copy_fail_family/skeletonkey_modules.c index 0927f10..a48acf4 100644 --- a/modules/copy_fail_family/skeletonkey_modules.c +++ b/modules/copy_fail_family/skeletonkey_modules.c @@ -247,6 +247,7 @@ const struct skeletonkey_module copy_fail_module = { .detect_sigma = copy_fail_family_sigma, .detect_yara = copy_fail_family_yara, .detect_falco = copy_fail_family_falco, + .opsec_notes = "Family-shared infrastructure (copy_fail, copy_fail_gcm, dirty_frag_esp/esp6, dirty_frag_rxrpc): all exploit a page-cache write primitive against /etc/passwd (UID flip to all-zeros) or install a persistent backdoor. Audit-visible via socket(AF_ALG) (a0=38), setsockopt(XFRM), AF_UNIX setup. Detection rules watch /etc/passwd, /etc/shadow, /etc/sudoers, /usr/bin/su for non-root writes. Family mitigation blacklists algif_aead/esp4/esp6/rxrpc and sets apparmor_restrict_unprivileged_userns=1. Cleanup evicts /etc/passwd from page cache and reverts mitigation conf.", }; /* ----- copy_fail_gcm (variant, no CVE) ----- */ @@ -279,6 +280,7 @@ const struct skeletonkey_module copy_fail_gcm_module = { .detect_sigma = copy_fail_family_sigma, .detect_yara = copy_fail_family_yara, .detect_falco = copy_fail_family_falco, + .opsec_notes = "Family-shared infrastructure (copy_fail, copy_fail_gcm, dirty_frag_esp/esp6, dirty_frag_rxrpc): all exploit a page-cache write primitive against /etc/passwd (UID flip to all-zeros) or install a persistent backdoor. Audit-visible via socket(AF_ALG) (a0=38), setsockopt(XFRM), AF_UNIX setup. Detection rules watch /etc/passwd, /etc/shadow, /etc/sudoers, /usr/bin/su for non-root writes. Family mitigation blacklists algif_aead/esp4/esp6/rxrpc and sets apparmor_restrict_unprivileged_userns=1. Cleanup evicts /etc/passwd from page cache and reverts mitigation conf.", }; /* ----- dirty_frag_esp (CVE-2026-43284 v4) ----- */ @@ -311,6 +313,7 @@ const struct skeletonkey_module dirty_frag_esp_module = { .detect_sigma = copy_fail_family_sigma, .detect_yara = copy_fail_family_yara, .detect_falco = copy_fail_family_falco, + .opsec_notes = "Family-shared infrastructure (copy_fail, copy_fail_gcm, dirty_frag_esp/esp6, dirty_frag_rxrpc): all exploit a page-cache write primitive against /etc/passwd (UID flip to all-zeros) or install a persistent backdoor. Audit-visible via socket(AF_ALG) (a0=38), setsockopt(XFRM), AF_UNIX setup. Detection rules watch /etc/passwd, /etc/shadow, /etc/sudoers, /usr/bin/su for non-root writes. Family mitigation blacklists algif_aead/esp4/esp6/rxrpc and sets apparmor_restrict_unprivileged_userns=1. Cleanup evicts /etc/passwd from page cache and reverts mitigation conf.", }; /* ----- dirty_frag_esp6 (CVE-2026-43284 v6) ----- */ @@ -343,6 +346,7 @@ const struct skeletonkey_module dirty_frag_esp6_module = { .detect_sigma = copy_fail_family_sigma, .detect_yara = copy_fail_family_yara, .detect_falco = copy_fail_family_falco, + .opsec_notes = "Family-shared infrastructure (copy_fail, copy_fail_gcm, dirty_frag_esp/esp6, dirty_frag_rxrpc): all exploit a page-cache write primitive against /etc/passwd (UID flip to all-zeros) or install a persistent backdoor. Audit-visible via socket(AF_ALG) (a0=38), setsockopt(XFRM), AF_UNIX setup. Detection rules watch /etc/passwd, /etc/shadow, /etc/sudoers, /usr/bin/su for non-root writes. Family mitigation blacklists algif_aead/esp4/esp6/rxrpc and sets apparmor_restrict_unprivileged_userns=1. Cleanup evicts /etc/passwd from page cache and reverts mitigation conf.", }; /* ----- dirty_frag_rxrpc (CVE-2026-43500) ----- */ @@ -375,6 +379,7 @@ const struct skeletonkey_module dirty_frag_rxrpc_module = { .detect_sigma = copy_fail_family_sigma, .detect_yara = copy_fail_family_yara, .detect_falco = copy_fail_family_falco, + .opsec_notes = "Family-shared infrastructure (copy_fail, copy_fail_gcm, dirty_frag_esp/esp6, dirty_frag_rxrpc): all exploit a page-cache write primitive against /etc/passwd (UID flip to all-zeros) or install a persistent backdoor. Audit-visible via socket(AF_ALG) (a0=38), setsockopt(XFRM), AF_UNIX setup. Detection rules watch /etc/passwd, /etc/shadow, /etc/sudoers, /usr/bin/su for non-root writes. Family mitigation blacklists algif_aead/esp4/esp6/rxrpc and sets apparmor_restrict_unprivileged_userns=1. Cleanup evicts /etc/passwd from page cache and reverts mitigation conf.", }; /* ----- Family registration ----- */ diff --git a/modules/dirty_cow_cve_2016_5195/skeletonkey_modules.c b/modules/dirty_cow_cve_2016_5195/skeletonkey_modules.c index 978795d..1f5d863 100644 --- a/modules/dirty_cow_cve_2016_5195/skeletonkey_modules.c +++ b/modules/dirty_cow_cve_2016_5195/skeletonkey_modules.c @@ -404,6 +404,7 @@ const struct skeletonkey_module dirty_cow_module = { .detect_sigma = dirty_cow_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Two-thread race: Thread A loops pwrite(/proc/self/mem) at the user's UID offset in /etc/passwd; Thread B loops madvise(MADV_DONTNEED) on a PRIVATE mmap of /etc/passwd. Overwrites the UID field with all-zeros, then execlp('su') to claim root. UID offset is parsed from the file, not hardcoded. Audit-visible via open(/proc/self/mem) + write + madvise(MADV_DONTNEED) bursts + /etc/passwd page-cache poisoning. Cleanup callback calls posix_fadvise(POSIX_FADV_DONTNEED) on /etc/passwd and writes 3 to /proc/sys/vm/drop_caches to evict.", }; void skeletonkey_register_dirty_cow(void) diff --git a/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c b/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c index 8097591..f2b0e9e 100644 --- a/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c +++ b/modules/dirty_pipe_cve_2022_0847/skeletonkey_modules.c @@ -522,6 +522,7 @@ const struct skeletonkey_module dirty_pipe_module = { .detect_sigma = dirty_pipe_sigma, .detect_yara = dirty_pipe_yara, .detect_falco = dirty_pipe_falco, + .opsec_notes = "Creates a pipe, fills+drains to leave PIPE_BUF_FLAG_CAN_MERGE on every slot; finds the UID offset in /etc/passwd by parsing the file; splice(1 byte) from (target_offset-1) to inherit the stale flag, then write(pipe) with the all-zero payload - kernel merges into the file's page cache. Offset must be non-page-aligned and the write must fit in a single page. Audit-visible via splice(fd=/etc/passwd) + write from a non-root process. --active mode writes/reads /tmp/skeletonkey-dirty-pipe-probe-XXXXXX to verify. Cleanup callback evicts /etc/passwd via posix_fadvise + drop_caches.", }; void skeletonkey_register_dirty_pipe(void) diff --git a/modules/dirtydecrypt_cve_2026_31635/skeletonkey_modules.c b/modules/dirtydecrypt_cve_2026_31635/skeletonkey_modules.c index 9d6323c..31e5fc2 100644 --- a/modules/dirtydecrypt_cve_2026_31635/skeletonkey_modules.c +++ b/modules/dirtydecrypt_cve_2026_31635/skeletonkey_modules.c @@ -1005,6 +1005,7 @@ const struct skeletonkey_module dirtydecrypt_module = { .detect_sigma = dd_sigma, .detect_yara = dd_yara, .detect_falco = dd_falco, + .opsec_notes = "Forked child runs unshare(CLONE_NEWUSER|CLONE_NEWNET); creates AF_RXRPC socket; builds an rxgk XDR token via add_key(SYS_add_key, 'rxrpc'); sets up loopback UDP server + rxrpc client; forges rxrpc DATA packets and fires 10000+ splice-based writes in a sliding window to overwrite a target setuid binary's page cache with a 120-byte ET_DYN ELF (setuid(0) + execve('/bin/sh')). Payload is never written to disk. Audit-visible via socket(AF_RXRPC) (a0=33) + add_key('rxrpc') + splice() bursts. Records target path to /tmp/skeletonkey-dirtydecrypt.target. Cleanup callback evicts candidate targets (/usr/bin/su et al) via drop_caches.", }; void skeletonkey_register_dirtydecrypt(void) diff --git a/modules/entrybleed_cve_2023_0458/skeletonkey_modules.c b/modules/entrybleed_cve_2023_0458/skeletonkey_modules.c index 81babf4..a1e8ee7 100644 --- a/modules/entrybleed_cve_2023_0458/skeletonkey_modules.c +++ b/modules/entrybleed_cve_2023_0458/skeletonkey_modules.c @@ -288,6 +288,7 @@ const struct skeletonkey_module entrybleed_module = { .detect_sigma = entrybleed_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Pure timing side-channel: rdtsc + prefetchnta sweep across the kernel high-half (~16 MiB) to time which 2 MiB page is mapped (entry_SYSCALL_64) and subtract its known offset from kbase. No syscalls fired, no file artifacts, no network. Classic auditd cannot see it; perf-counter EDR can flag a process spending unusual time in tight prefetchnta loops but classic rules will not. No cleanup needed.", }; void skeletonkey_register_entrybleed(void) diff --git a/modules/fragnesia_cve_2026_46300/skeletonkey_modules.c b/modules/fragnesia_cve_2026_46300/skeletonkey_modules.c index 4485928..18c010f 100644 --- a/modules/fragnesia_cve_2026_46300/skeletonkey_modules.c +++ b/modules/fragnesia_cve_2026_46300/skeletonkey_modules.c @@ -1210,6 +1210,7 @@ const struct skeletonkey_module fragnesia_module = { .detect_sigma = fg_sigma, .detect_yara = fg_yara, .detect_falco = fg_falco, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + socket(AF_ALG, SOCK_SEQPACKET) for an AES-GCM keystream table; NETLINK_XFRM setsockopt to install ESP-in-TCP state; TCP_ULP setsockopt on a loopback connection; splice() from a carrier setuid binary (/usr/bin/su or /bin/su) into the TCP socket. Artifacts: /tmp/skeletonkey-fragnesia-probe-XXXXXX (mkstemp, unlinked after probe) and /tmp/skeletonkey-fragnesia.target. Audit-visible via socket(AF_ALG) (38), NETLINK_XFRM (6) writes, TCP_ULP setsockopt, splice() of setuid binary. No external network (loopback). Cleanup callback unlinks /tmp files and evicts the carrier from page cache.", }; void skeletonkey_register_fragnesia(void) diff --git a/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c b/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c index b6c816a..5ac8651 100644 --- a/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c +++ b/modules/fuse_legacy_cve_2022_0185/skeletonkey_modules.c @@ -885,6 +885,7 @@ const struct skeletonkey_module fuse_legacy_module = { .detect_sigma = fuse_legacy_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNS) for CAP_SYS_ADMIN; fsopen('cgroup2') + multiple fsconfig(FSCONFIG_SET_STRING, 'source', ...) calls to overflow legacy_parse_param's buffer. OOB write lands in kmalloc-4k adjacent to a msg_msg groom. No persistent files (msg_msg lives in the IPC namespace which disappears with the child). Dmesg silent on success; KASAN would show slab corruption if enabled. Audit-visible via unshare(CLONE_NEWUSER|CLONE_NEWNS) + fsopen + fsconfig pattern in a single process. No cleanup callback - IPC queues auto-drain on namespace exit.", }; void skeletonkey_register_fuse_legacy(void) diff --git a/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c b/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c index 9833d6a..bd4a327 100644 --- a/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c +++ b/modules/netfilter_xtcompat_cve_2021_22555/skeletonkey_modules.c @@ -974,6 +974,7 @@ const struct skeletonkey_module netfilter_xtcompat_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + setsockopt(SOL_IP, IPT_SO_SET_REPLACE) with a malformed xt_entry_target to trigger xt_compat_target_to_user 4-byte OOB into kmalloc-2k. msg_msg + sk_buff cross-cache groom. Writes /tmp/skeletonkey-xtcompat.log (breadcrumb). Audit-visible via unshare + setsockopt(IPT_SO_SET_REPLACE) + msgsnd/msgrcv + sendmmsg(sk_buff spray). Dmesg silent on success; KASAN oops if the groom misses. Cleanup callback unlinks the log; IPC auto-drains on namespace exit.", }; void skeletonkey_register_netfilter_xtcompat(void) diff --git a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c index b7ba6ff..5e1ee1a 100644 --- a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c +++ b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c @@ -1137,6 +1137,7 @@ const struct skeletonkey_module nf_tables_module = { .detect_sigma = nf_tables_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + nfnetlink batch (NEWTABLE + NEWCHAIN/LOCAL_OUT + NEWSET verdict-key + NEWSETELEM malformed NFT_GOTO) committed twice to trigger the nft_verdict_init double-free. msg_msg cg-96 groom with forged pipapo_elem headers; --full-chain sprays kaddr-tagged forged elems and re-fires. Writes /tmp/skeletonkey-nft_set_uaf.log (conditional). Audit-visible via unshare + socket(NETLINK_NETFILTER) + sendmsg batches + msgget/msgsnd. Dmesg: KASAN double-free panic on vulnerable kernels; silent otherwise. Cleanup is finisher-gated; no persistent files on success.", }; void skeletonkey_register_nf_tables(void) diff --git a/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c b/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c index d3ed681..d2e360a 100644 --- a/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c +++ b/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c @@ -1042,6 +1042,7 @@ const struct skeletonkey_module nft_fwd_dup_module = { .detect_sigma = nft_fwd_dup_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + nfnetlink batch (NEWTABLE netdev + NEWCHAIN HW_OFFLOAD + NEWRULE with 16 immediate(NF_ACCEPT) + 1 fwd). Offload hook walks the rule advertising num_actions+=16 but allocates only the original-actions size -> OOB write at entries[16] into adjacent kmalloc-512. msg_msg groom tagged 'SKELETONKEY_FWD'. Writes /tmp/skeletonkey-nft_fwd_dup.log. Audit-visible via unshare + socket(NETLINK_NETFILTER) + sendmsg + ioctl(SIOCGIFFLAGS/SIOCSIFFLAGS loopback) + msgsnd. Dmesg: KASAN or silent. Cleanup callback drains IPC queues and unlinks log.", }; void skeletonkey_register_nft_fwd_dup(void) diff --git a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c index 1d460db..74ba8e9 100644 --- a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c +++ b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c @@ -1153,6 +1153,7 @@ const struct skeletonkey_module nft_payload_module = { .detect_sigma = nft_payload_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + nfnetlink batch (NEWTABLE + NEWCHAIN/LOCAL_OUT + NEWSET with oversized NFTA_SET_DESC + NEWSETELEM whose NFTA_PAYLOAD_SREG = attacker verdict code). On packet eval, regs->verdict.code is used unchecked as index into regs->data[] -> OOB. Dual-slab groom (kmalloc-1k + kmalloc-cg-96). Trigger via sendto(AF_INET, 127.0.0.1:31337). Writes /tmp/skeletonkey-nft_payload.log. Audit-visible via unshare + socket(NETLINK_NETFILTER) + sendmsg + msgsnd + socket(AF_INET)/sendto. Cleanup callback unlinks log.", }; void skeletonkey_register_nft_payload(void) diff --git a/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c b/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c index 082235e..d4c8056 100644 --- a/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c +++ b/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c @@ -1035,6 +1035,7 @@ const struct skeletonkey_module nft_set_uaf_module = { .detect_sigma = nft_set_uaf_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNET) + single nfnetlink transaction: NEWTABLE + NEWCHAIN + NEWSET (anonymous, ANONYMOUS|CONSTANT|EVAL) + NEWRULE with nft_lookup referencing the anon set + DELSET + DELRULE. Vulnerable kernels do not deactivate the lookup's set ref on commit -> UAF when set frees. msg_msg cg-512 spray (32 queues x 16 msgs, tag 'SKELETONKEY_SET'). --full-chain re-fires with forged headers (data ptr = kaddr) and NEWSETELEM payload. Writes /tmp/skeletonkey-nft_set_uaf.log. Audit-visible via unshare + socket(NETLINK_NETFILTER) + sendmsg + msgsnd. Dmesg: KASAN oops on UAF. Cleanup unlinks log.", }; void skeletonkey_register_nft_set_uaf(void) diff --git a/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c b/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c index 10eed7e..54e3244 100644 --- a/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c +++ b/modules/overlayfs_cve_2021_3493/skeletonkey_modules.c @@ -505,6 +505,7 @@ const struct skeletonkey_module overlayfs_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNS) for CAP_SYS_ADMIN; mount('overlay', merged, ...); compile + copy payload into the merged dir (writes upper on host fs); setxattr(upper_payload, 'security.capability', cap_setuid+ep) - the bug is that this xattr persists on the HOST fs despite being set inside userns. Parent then execve's the now-CAP_SETUID payload, calls setuid(0), execs /bin/sh. Artifacts: /tmp/skeletonkey-ovl-XXXXXX/ workdir; cleaned on exit/failure (on success the exec replaces the process so cleanup does not run). Audit-visible via unshare + mount(overlay) + setxattr(security.capability) + execve of attacker-controlled binary. Dmesg silent.", }; void skeletonkey_register_overlayfs(void) diff --git a/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c b/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c index 2428a9e..db8b8ac 100644 --- a/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c +++ b/modules/overlayfs_setuid_cve_2023_0386/skeletonkey_modules.c @@ -421,6 +421,7 @@ const struct skeletonkey_module overlayfs_setuid_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "unshare(CLONE_NEWUSER|CLONE_NEWNS) + overlayfs mount with a setuid-root binary in lower (e.g. /usr/bin/su); chown on the merged view triggers copy-up that preserves the setuid bit in upper - but upper is owned by the unprivileged user. Overwrites upper-layer contents with attacker payload and execve's for root. Artifacts: /tmp/skeletonkey-ovlsu-XXXXXX/ (workdir with payload.c, binary, overlay mounts); cleanup callback removes these. Audit-visible via unshare(CLONE_NEWUSER|CLONE_NEWNS) + mount(overlay) + chown on the merged view. No network. Dmesg silent on success.", }; void skeletonkey_register_overlayfs_setuid(void) diff --git a/modules/pack2theroot_cve_2026_41651/skeletonkey_modules.c b/modules/pack2theroot_cve_2026_41651/skeletonkey_modules.c index 5b559f8..58fd09a 100644 --- a/modules/pack2theroot_cve_2026_41651/skeletonkey_modules.c +++ b/modules/pack2theroot_cve_2026_41651/skeletonkey_modules.c @@ -790,6 +790,7 @@ const struct skeletonkey_module pack2theroot_module = { .detect_sigma = p2tr_sigma, .detect_yara = p2tr_yara, .detect_falco = p2tr_falco, + .opsec_notes = "TOCTOU race in PackageKit's polkit-auth + D-Bus InstallFiles dispatcher: sends back-to-back async calls (first with SIMULATE to bypass polkit, second with the malicious .deb) so the cached flags are overwritten before the idle callback fires. Builds a minimal .deb ar archive in pure C with a postinst that installs a setuid bash. Writes /tmp/.pk-dummy-.deb, /tmp/.pk-payload-.deb, and /tmp/skeletonkey-pack2theroot.state; via the polkit-bypassed postinst plants /tmp/.suid_bash setuid root. Audit-visible via dpkg execve from packagekitd for a non-root caller, chmod(2) on /tmp/.suid_bash, creat/openat on the .deb files. Cleanup callback unlinks the .debs and best-effort removes /tmp/.suid_bash (which is owned by root).", }; void skeletonkey_register_pack2theroot(void) diff --git a/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c b/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c index 436a0b8..3f39885 100644 --- a/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c +++ b/modules/ptrace_traceme_cve_2019_13272/skeletonkey_modules.c @@ -331,6 +331,7 @@ const struct skeletonkey_module ptrace_traceme_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Parent and child cooperate: child calls ptrace(PTRACE_TRACEME) (recording the parent's current credentials), then sleeps; parent execve's a setuid binary (pkexec or su) and elevates. The stale ptrace_link in the child still holds the old (non-root) credentials, so PTRACE_ATTACH succeeds against the now-root parent; the child injects shellcode at the parent's RIP via PTRACE_POKETEXT and detaches. Audit-visible via ptrace with a0=0 (PTRACE_TRACEME) closely followed by execve of a setuid binary in the parent process. No file artifacts; no persistent changes. No cleanup callback - the exploit execs /bin/sh and does not return.", }; void skeletonkey_register_ptrace_traceme(void) diff --git a/modules/pwnkit_cve_2021_4034/skeletonkey_modules.c b/modules/pwnkit_cve_2021_4034/skeletonkey_modules.c index 5e15e63..ca81909 100644 --- a/modules/pwnkit_cve_2021_4034/skeletonkey_modules.c +++ b/modules/pwnkit_cve_2021_4034/skeletonkey_modules.c @@ -472,6 +472,7 @@ const struct skeletonkey_module pwnkit_module = { .detect_sigma = pwnkit_sigma, .detect_yara = pwnkit_yara, .detect_falco = pwnkit_falco, + .opsec_notes = "Invokes pkexec with argc==0 so the first envp slot is misread as argv[0]; pkexec's iconv-during-decoding loads attacker .so via dlopen by way of crafted GCONV_PATH + CHARSET env vars. Builds a gconv payload .so and gconv-modules cache in /tmp/skeletonkey-pwnkit-XXXXXX (compiles via fork/execl of gcc). Audit-visible via execve(/usr/bin/pkexec) with GCONV_PATH and CHARSET set. No network. Cleanup callback removes /tmp/skeletonkey-pwnkit-* (on failure path; on success the exec replaces the process).", }; void skeletonkey_register_pwnkit(void) diff --git a/modules/sequoia_cve_2021_33909/skeletonkey_modules.c b/modules/sequoia_cve_2021_33909/skeletonkey_modules.c index 5ece9f1..5d57d99 100644 --- a/modules/sequoia_cve_2021_33909/skeletonkey_modules.c +++ b/modules/sequoia_cve_2021_33909/skeletonkey_modules.c @@ -700,6 +700,7 @@ const struct skeletonkey_module sequoia_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Builds ~5000 nested directories under /tmp/skeletonkey-sequoia (each name 200 'A' chars); enters userns for CAP_SYS_ADMIN; bind-mounts the leaf over itself to amplify the rendered mountinfo string length; reads /proc/self/mountinfo to trigger the int-vs-size_t overflow in seq_buf_alloc(), producing an OOB write of mountinfo bytes off the stack buffer. Artifacts: /tmp/skeletonkey-sequoia/ (deep tree + bind mounts) and /tmp/skeletonkey-sequoia.log (byte count + dmesg sample). Audit-visible via unshare(CLONE_NEWUSER|CLONE_NEWNS) + mount() + burst of ~5000 mkdir/mkdirat. No network. Cleanup callback walks back down the tree, unmounts, removes dirs, unlinks the .log.", }; void skeletonkey_register_sequoia(void) diff --git a/modules/stackrot_cve_2023_3269/skeletonkey_modules.c b/modules/stackrot_cve_2023_3269/skeletonkey_modules.c index d8c1242..b8fd2f5 100644 --- a/modules/stackrot_cve_2023_3269/skeletonkey_modules.c +++ b/modules/stackrot_cve_2023_3269/skeletonkey_modules.c @@ -966,6 +966,7 @@ const struct skeletonkey_module stackrot_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Child forks, enters userns, builds a race region with MAP_GROWSDOWN + anchor VMAs, sprays kmalloc-192 with msg_msg payloads, then spawns Thread A (mremap/munmap of region boundary to rotate maple-tree nodes) + Thread B (fork+fault the growsdown region to deref freed node). UAF in __vma_adjust fires if a sprayed msg_msg reclaims the freed node. Writes /tmp/skeletonkey-stackrot.log (iteration counts + slab delta). Audit-visible via unshare + mremap/munmap bursts on stack regions + msgsnd spray. No network. Cleanup callback unlinks /tmp log.", }; void skeletonkey_register_stackrot(void) diff --git a/modules/sudo_samedit_cve_2021_3156/skeletonkey_modules.c b/modules/sudo_samedit_cve_2021_3156/skeletonkey_modules.c index bbb05e6..7c4be74 100644 --- a/modules/sudo_samedit_cve_2021_3156/skeletonkey_modules.c +++ b/modules/sudo_samedit_cve_2021_3156/skeletonkey_modules.c @@ -488,6 +488,7 @@ const struct skeletonkey_module sudo_samedit_module = { .detect_sigma = sudo_samedit_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Invokes sudoedit with argv = { 'sudoedit', '-s', trailing-backslash, then ~60 padding args each ending in backslash }; the parser's unescape loop in set_cmnd() walks past the end of the argv string for the trailing-backslash argument, copying adjacent stack/env into an undersized heap buffer. Audit-visible via execve(/usr/bin/sudoedit) with -s and a trailing-backslash argv. No persistent file artifacts (only best-effort removal of /tmp/.sudo_edit_*). No network. Dmesg silent unless sudo crashes (SIGSEGV). Per-distro heap layout determines landing; verifies geteuid()==0 afterward.", }; void skeletonkey_register_sudo_samedit(void) { skeletonkey_register(&sudo_samedit_module); } diff --git a/modules/sudoedit_editor_cve_2023_22809/skeletonkey_modules.c b/modules/sudoedit_editor_cve_2023_22809/skeletonkey_modules.c index 65eeefe..83de2ce 100644 --- a/modules/sudoedit_editor_cve_2023_22809/skeletonkey_modules.c +++ b/modules/sudoedit_editor_cve_2023_22809/skeletonkey_modules.c @@ -632,6 +632,7 @@ const struct skeletonkey_module sudoedit_editor_module = { .detect_sigma = sudoedit_editor_sigma, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Sets EDITOR=' -- /etc/passwd' so sudoedit splits on the literal '--' and treats /etc/passwd as an additional editable file. Compiled helper appends 'skel::0:0:skeletonkey:/root:/bin/sh' to the post-'--' target; sudoedit runs the helper as root and copies back. Artifacts: /tmp/skeletonkey-sudoedit-XXXXXX (helper.c, helper binary, optional passwd.before backup); /etc/passwd gets the new 'skel' entry; drops root via 'su skel'. Audit-visible via execve(/usr/bin/sudoedit) with EDITOR/VISUAL/SUDO_EDITOR containing the literal '--' token. No network. Cleanup callback restores /etc/passwd from backup (if root) or removes the 'skel' line, and removes the /tmp dir.", }; void skeletonkey_register_sudoedit_editor(void) diff --git a/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c b/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c index 7a944e1..ed4d990 100644 --- a/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c +++ b/modules/vmwgfx_cve_2023_2008/skeletonkey_modules.c @@ -721,6 +721,7 @@ const struct skeletonkey_module vmwgfx_module = { .detect_sigma = NULL, .detect_yara = NULL, .detect_falco = NULL, + .opsec_notes = "Opens /dev/dri/card* (vmwgfx DRM - only reachable on VMware guests); DRM_IOCTL_VMW_CREATE_DMABUF with size=4096+16 lands in the kmalloc-512 page-count bucket but the byte-length overruns during kunmap_atomic copy in ttm_bo_kmap; mmap + write recognizable pattern across page boundary; UNREF commits the OOB into adjacent kmalloc-512. msg_msg spray tagged 'SKVMWGFX'. Writes /tmp/skeletonkey-vmwgfx.log (slab counts pre/post, trigger success). Audit-visible via openat(/dev/dri/card*), ioctl(0xc010644a CREATE / 0x4004644b UNREF), msgsnd spray. No network. Cleanup callback unlinks /tmp log; --full-chain re-seeds spray with kaddr-tagged payloads and the modprobe_path finisher arbitrates via 3s sentinel.", }; void skeletonkey_register_vmwgfx(void)