verify-vm: close the loop — first successful end-to-end VM verification
Five fixes that landed us at a working 'verify.sh <module> -> 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
<linux/netfilter/nf_tables.h>.
3. tools/verify-vm/Vagrantfile — wrap config in 'c.vm.define host do
|m| ... end' block so 'vagrant up <skk-MODULE>' 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.
This commit is contained in:
@@ -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 <linux/netfilter/nf_tables.h>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKELETONKEY_NFT_COMPAT_H
|
||||||
|
#define SKELETONKEY_NFT_COMPAT_H
|
||||||
|
|
||||||
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
|
||||||
|
/* ── 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 */
|
||||||
@@ -88,6 +88,7 @@
|
|||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netfilter/nf_tables.h>
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
#include "../../core/nft_compat.h" /* shims for newer-kernel uapi constants */
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
* Kernel-range table
|
* Kernel-range table
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netfilter/nf_tables.h>
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
#include "../../core/nft_compat.h"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
* Kernel range table — fixes per branch.
|
* Kernel range table — fixes per branch.
|
||||||
|
|||||||
@@ -80,6 +80,7 @@
|
|||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netfilter/nf_tables.h>
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
#include "../../core/nft_compat.h"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* ------------------------------------------------------------------
|
||||||
* Kernel-range table
|
* Kernel-range table
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netfilter/nf_tables.h>
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
#include "../../core/nft_compat.h"
|
||||||
|
|
||||||
/* NFT_SET_EVAL was added in 5.6; older UAPI headers may not define it.
|
/* 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,
|
* Anonymous-set + lookup exploit shape works on builds with this flag,
|
||||||
|
|||||||
@@ -149,11 +149,20 @@ fi
|
|||||||
|
|
||||||
# Run the explain probe.
|
# Run the explain probe.
|
||||||
LOG="$LOG_DIR/verify-${MODULE}-$(date +%Y%m%d-%H%M%S).log"
|
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..."
|
echo "[*] running verifier..."
|
||||||
vagrant provision "$VM_HOSTNAME" --provision-with build-and-verify 2>&1 | tee "$LOG"
|
vagrant provision "$VM_HOSTNAME" --provision-with build-and-verify 2>&1 | tee "$LOG"
|
||||||
|
|
||||||
# Parse verdict.
|
# Parse verdict. Vagrant prefixes provisioner output with the VM name
|
||||||
VERDICT=$(grep -E "^VERDICT: " "$LOG" | tail -1 | awk '{print $2}')
|
# (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="?"
|
[[ -z "$VERDICT" ]] && VERDICT="?"
|
||||||
|
|
||||||
# Compare.
|
# Compare.
|
||||||
|
|||||||
Reference in New Issue
Block a user