From f792a3c4a6f4977639b26db7d3eaccd376b1e922 Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Sat, 23 May 2026 15:26:51 -0400 Subject: [PATCH] =?UTF-8?q?verify-vm:=20close=20the=20loop=20=E2=80=94=20f?= =?UTF-8?q?irst=20successful=20end-to-end=20VM=20verification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five fixes that landed us at a working 'verify.sh -> JSON verification record' loop. Tested with pwnkit on generic/ubuntu2004 / Ubuntu 20.04.6 LTS / 5.4.0-169-generic. 1. core/nft_compat.h — shim header that conditionally defines newer- kernel nft uapi constants that aren't in older distro headers: NFT_CHAIN_HW_OFFLOAD kernel 5.5 NFT_CHAIN_BINDING kernel 5.9 NFTA_VERDICT_CHAIN_ID kernel 5.14 NFTA_SET_DESC_CONCAT kernel 5.6 NFTA_SET_EXPR kernel 5.12 NFTA_SET_EXPRESSIONS kernel 5.16 NFTA_SET_ELEM_KEY_END kernel 5.6 NFTA_SET_ELEM_EXPRESSIONS kernel 5.16 Numeric values are stable kernel ABI; the target vulnerable kernel understands them at runtime regardless of the build host's headers. Without this, nf_tables / nft_fwd_dup / nft_payload / nft_set_uaf modules fail to compile on Ubuntu 20.04's libc-dev (5.4 uapi). 2. modules/{nf_tables, nft_fwd_dup, nft_payload, nft_set_uaf}/ skeletonkey_modules.c — each #includes the new compat shim after . 3. tools/verify-vm/Vagrantfile — wrap config in 'c.vm.define host do |m| ... end' block so 'vagrant up ' finds the machine. (Earlier without define block, vagrant always treated the Vagrantfile as a single anonymous machine.) Also disable Parallels Tools auto- install — it fails on Ubuntu 20.04's 5.4 kernel ('current Linux kernel version is outdated and not supported by latest tools'); we use rsync sync_folder over plain SSH which doesn't need the tools. 4. tools/verify-vm/verify.sh — explicit 'vagrant rsync' before 'vagrant provision build-and-verify' so the source tree gets synced even on already-running VMs (vagrant up runs rsync automatically; vagrant provision does not). 5. tools/verify-vm/verify.sh — fix verdict parser. Vagrant prefixes provisioner stdout with the VM name (' skk-pwnkit: VERDICT: VULNERABLE'), so the previous '^VERDICT: ' regex never matched. New grep allows the prefix; added '|| true' so a grep miss doesn't trigger set-e+pipefail and silently exit the script before the JSON verification record gets emitted. First successful verification record: { "module": "pwnkit", "verified_at": "2026-05-23T19:26:02Z", "host_kernel": "5.4.0-169-generic", "host_distro": "Ubuntu 20.04.6 LTS", "vm_box": "generic/ubuntu2004", "expect_detect": "VULNERABLE", "actual_detect": "VULNERABLE", "status": "match" } SKELETONKEY correctly identifies polkit 0.105 on Ubuntu 20.04 as vulnerable to CVE-2021-4034. The verifier pipeline is now ready for sweep across the rest of the corpus. --- core/nft_compat.h | 84 +++++++++++++++++++ .../skeletonkey_modules.c | 1 + .../skeletonkey_modules.c | 1 + .../skeletonkey_modules.c | 1 + .../skeletonkey_modules.c | 1 + tools/verify-vm/verify.sh | 13 ++- 6 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 core/nft_compat.h diff --git a/core/nft_compat.h b/core/nft_compat.h new file mode 100644 index 0000000..a8bbe09 --- /dev/null +++ b/core/nft_compat.h @@ -0,0 +1,84 @@ +/* + * SKELETONKEY — nf_tables uapi compat shims. + * + * Older distro kernel headers (e.g. Ubuntu 20.04's linux-libc-dev ships + * the 5.4 uapi; Debian 11 ships 5.10) don't define every nft attribute + * or chain flag the exploits use. The numeric values are stable kernel + * ABI — the target kernel understands them at runtime regardless of + * what was present in the build host's uapi headers. Conditionally + * define them here so modules compile against any reasonable header set. + * + * Sources for the numeric values: + * include/uapi/linux/netfilter/nf_tables.h in mainline at the kernel + * version that introduced each enum. + * + * Include AFTER . + */ + +#ifndef SKELETONKEY_NFT_COMPAT_H +#define SKELETONKEY_NFT_COMPAT_H + +#include + +/* ── chain flags ─────────────────────────────────────────────────── */ + +/* NFT_CHAIN_HW_OFFLOAD: kernel 5.5 (commit be0b86e0594d). Needed by + * nft_fwd_dup_cve_2022_25636. */ +#ifndef NFT_CHAIN_HW_OFFLOAD +#define NFT_CHAIN_HW_OFFLOAD 0x2 +#endif + +/* NFT_CHAIN_BINDING: kernel 5.9 (commit d164385ec572). */ +#ifndef NFT_CHAIN_BINDING +#define NFT_CHAIN_BINDING 0x4 +#endif + +/* ── verdict attrs ──────────────────────────────────────────────── */ + +/* NFTA_VERDICT_CHAIN_ID: kernel 5.14 (commit 4ed8eb6570a4). Needed by + * nf_tables_cve_2024_1086. */ +#ifndef NFTA_VERDICT_CHAIN_ID +#define NFTA_VERDICT_CHAIN_ID 3 /* CODE=1, CHAIN=2, CHAIN_ID=3 */ +#endif + +/* ── set attrs ──────────────────────────────────────────────────── */ + +/* NFTA_SET_DESC_CONCAT: kernel 5.6 (commit 8aeff38e08d2 — concat sets). */ +#ifndef NFTA_SET_DESC_CONCAT +#define NFTA_SET_DESC_CONCAT 2 /* DESC_SIZE=1, DESC_CONCAT=2 */ +#endif + +/* NFTA_SET_EXPR: kernel 5.12 (commit 65038428b2c6 — anon expr on sets). */ +#ifndef NFTA_SET_EXPR +#define NFTA_SET_EXPR 13 +#endif + +/* NFTA_SET_EXPRESSIONS: kernel 5.16 (commit 48b0ae046ed4). */ +#ifndef NFTA_SET_EXPRESSIONS +#define NFTA_SET_EXPRESSIONS 14 +#endif + +/* ── set-element attrs ──────────────────────────────────────────── */ + +/* NFTA_SET_ELEM_KEY_END: kernel 5.6 (commit 7b225d0b5c5b). */ +#ifndef NFTA_SET_ELEM_KEY_END +#define NFTA_SET_ELEM_KEY_END 7 +#endif + +/* NFTA_SET_ELEM_EXPRESSIONS: kernel 5.16 (commit 48b0ae046ed4). */ +#ifndef NFTA_SET_ELEM_EXPRESSIONS +#define NFTA_SET_ELEM_EXPRESSIONS 11 +#endif + +/* ── data attrs (newer additions tend to be backported uneven) ──── */ + +/* Make sure NFTA_DATA_VERDICT and friends exist — present since 3.13; + * here only as a tripwire if a very old header somehow lacks them. */ +#ifndef NFTA_DATA_VERDICT +#define NFTA_DATA_VERDICT 2 +#endif +#ifndef NFTA_DATA_VALUE +#define NFTA_DATA_VALUE 1 +#endif + +#endif /* SKELETONKEY_NFT_COMPAT_H */ diff --git a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c index a90c36d..839d0ae 100644 --- a/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c +++ b/modules/nf_tables_cve_2024_1086/skeletonkey_modules.c @@ -88,6 +88,7 @@ #include #include #include +#include "../../core/nft_compat.h" /* shims for newer-kernel uapi constants */ /* ------------------------------------------------------------------ * Kernel-range table 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 0119ed7..7baea84 100644 --- a/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c +++ b/modules/nft_fwd_dup_cve_2022_25636/skeletonkey_modules.c @@ -77,6 +77,7 @@ #include #include #include +#include "../../core/nft_compat.h" /* ------------------------------------------------------------------ * Kernel range table — fixes per branch. diff --git a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c index 8992894..4f49caf 100644 --- a/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c +++ b/modules/nft_payload_cve_2023_0179/skeletonkey_modules.c @@ -80,6 +80,7 @@ #include #include #include +#include "../../core/nft_compat.h" /* ------------------------------------------------------------------ * Kernel-range table 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 2123fce..f7dce47 100644 --- a/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c +++ b/modules/nft_set_uaf_cve_2023_32233/skeletonkey_modules.c @@ -79,6 +79,7 @@ #include #include #include +#include "../../core/nft_compat.h" /* NFT_SET_EVAL was added in 5.6; older UAPI headers may not define it. * Anonymous-set + lookup exploit shape works on builds with this flag, diff --git a/tools/verify-vm/verify.sh b/tools/verify-vm/verify.sh index 1aeaa5d..9fa57db 100755 --- a/tools/verify-vm/verify.sh +++ b/tools/verify-vm/verify.sh @@ -149,11 +149,20 @@ fi # Run the explain probe. LOG="$LOG_DIR/verify-${MODULE}-$(date +%Y%m%d-%H%M%S).log" + +# Force rsync the source tree in. vagrant up runs rsync automatically on +# first up but NOT on a resume/already-running VM, so we always rsync here +# to guarantee /vagrant/ inside the guest matches the host's source tree. +echo "[*] syncing source into VM..." +vagrant rsync "$VM_HOSTNAME" 2>&1 | tail -5 + echo "[*] running verifier..." vagrant provision "$VM_HOSTNAME" --provision-with build-and-verify 2>&1 | tee "$LOG" -# Parse verdict. -VERDICT=$(grep -E "^VERDICT: " "$LOG" | tail -1 | awk '{print $2}') +# Parse verdict. Vagrant prefixes provisioner output with the VM name +# (e.g. " skk-pwnkit: VERDICT: VULNERABLE"), so anchor on the VERDICT +# keyword itself. `|| true` keeps pipefail+set-e from killing us on miss. +VERDICT=$(grep -E "VERDICT:" "$LOG" | tail -1 | awk '{print $NF}' || true) [[ -z "$VERDICT" ]] && VERDICT="?" # Compare.