8ac041a295
release / build (arm64) (push) Waiting to run
release / build (x86_64) (push) Waiting to run
release / build (x86_64-static / musl) (push) Waiting to run
release / build (arm64-static / musl) (push) Waiting to run
release / release (push) Blocked by required conditions
Five more CVEs empirically confirmed end-to-end against real Linux VMs:
- CVE-2019-14287 sudo_runas_neg1 (Ubuntu 18.04 + sudoers grant)
- CVE-2020-29661 tioscpgrp (Ubuntu 20.04 pinned to 5.4.0-26)
- CVE-2024-26581 nft_pipapo (Ubuntu 22.04 + mainline 5.15.5)
- CVE-2025-32463 sudo_chwoot (Ubuntu 22.04 + sudo 1.9.16p1 from source)
- CVE-2025-6019 udisks_libblockdev (Debian 12 + udisks2 + polkit rule)
Required real plumbing work:
- Per-module provisioner hook (tools/verify-vm/provisioners/<module>.sh)
- Two-phase provision in verify.sh (prep → reboot if needed → verify)
fixes silent-fail where new kernel installed but VM never rebooted
- GRUB_DEFAULT pinning in both pin-kernel and pin-mainline blocks
(kernel downgrades like 5.4.0-169 → 5.4.0-26 now actually boot the target)
- Old-mainline URL fallback in pin-mainline (≤ 4.15 debs at /v$KVER/ not /amd64/)
mutagen_astronomy marked manual: true — mainline 4.14.70 kernel-panics on
Ubuntu 18.04's rootfs ('Failed to execute /init (error -8)' — kernel config
mismatch). Genuinely needs a CentOS 6 / Debian 7 image.
214 lines
9.5 KiB
Ruby
214 lines
9.5 KiB
Ruby
# -*- mode: ruby -*-
|
|
# vi: set ft=ruby :
|
|
#
|
|
# tools/verify-vm/Vagrantfile — parameterized verifier VM.
|
|
#
|
|
# Driven by env vars set by tools/verify-vm/verify.sh:
|
|
#
|
|
# SKK_VM_BOX generic/<box> name (e.g. generic/debian11)
|
|
# SKK_VM_KERNEL_PKG optional apt package for the vulnerable kernel
|
|
# (e.g. linux-image-5.13.0-19-generic). Empty = use stock.
|
|
# SKK_VM_KERNEL_VERSION expected kernel version after install
|
|
# SKK_VM_HOSTNAME hostname for this VM (used in vagrant box name)
|
|
#
|
|
# The Vagrantfile mounts the repo root at /vagrant (Vagrant default) so the
|
|
# in-VM `make` builds against your live source — no rebuild loop.
|
|
|
|
require "yaml"
|
|
|
|
REPO_ROOT = File.expand_path("../..", __dir__)
|
|
|
|
box = ENV["SKK_VM_BOX"] || "generic/debian12"
|
|
pkg = ENV["SKK_VM_KERNEL_PKG"] || ""
|
|
mainline = ENV["SKK_VM_MAINLINE_VERSION"] || ""
|
|
kver = ENV["SKK_VM_KERNEL_VERSION"] || ""
|
|
host = ENV["SKK_VM_HOSTNAME"] || "skk-verify"
|
|
|
|
Vagrant.configure("2") do |c|
|
|
# Define ONE Vagrant machine named after SKK_VM_HOSTNAME. Per-module
|
|
# isolation: each module gets its own `skk-<module>` machine that
|
|
# vagrant tracks in .vagrant/machines/skk-<module>/parallels/.
|
|
c.vm.define host do |m|
|
|
m.vm.box = box
|
|
# Guest hostnames forbid underscores per RFC 952. Vagrant machine
|
|
# names allow them (we keep skk-cgroup_release_agent so per-module
|
|
# state stays isolated in .vagrant/machines/), but inside the VM
|
|
# we translate to hyphens so the hostname is RFC-valid.
|
|
m.vm.hostname = host.gsub("_", "-")
|
|
|
|
m.vm.synced_folder REPO_ROOT, "/vagrant",
|
|
type: "rsync", rsync__exclude: ["build/", ".git/", "*.o", "skeletonkey-test*"]
|
|
|
|
m.vm.provider "parallels" do |p|
|
|
p.memory = 2048
|
|
p.cpus = 2
|
|
p.name = host
|
|
# Don't auto-update Parallels Tools: the installer fails on older
|
|
# guest kernels (e.g. Ubuntu 20.04's 5.4.0-169 is "outdated and
|
|
# not supported" by latest tools). We use rsync over SSH for
|
|
# sync_folder, which doesn't need the guest tools at all.
|
|
p.update_guest_tools = false
|
|
p.check_guest_tools = false
|
|
end
|
|
|
|
# 1. Always install build deps + sudo (needed for module verification).
|
|
m.vm.provision "shell", inline: <<-SHELL
|
|
set -e
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -qq
|
|
apt-get install -y -qq build-essential libglib2.0-dev pkg-config sudo curl ca-certificates
|
|
elif command -v dnf >/dev/null 2>&1; then
|
|
dnf install -y -q gcc make glib2-devel pkgconfig sudo curl
|
|
fi
|
|
SHELL
|
|
|
|
# 2a. Pin via apt if requested. Reboot needed afterward.
|
|
if !pkg.empty?
|
|
m.vm.provision "shell", name: "pin-kernel-#{pkg}", inline: <<-SHELL
|
|
set -e
|
|
if dpkg-query -W -f='${Status}' #{pkg} 2>/dev/null | grep -q 'install ok installed'; then
|
|
echo "[=] #{pkg} already installed"
|
|
else
|
|
echo "[+] installing #{pkg} (kernel target #{kver})"
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get install -y -qq #{pkg}
|
|
echo "[i] kernel #{pkg} installed"
|
|
fi
|
|
# Pin grub default to this specific kernel. Without it, grub
|
|
# picks the highest-versioned kernel installed (typically a
|
|
# stock HWE backport that's POST-fix), defeating the pin's
|
|
# purpose. Find the kver string by stripping linux-image-
|
|
# prefix from the pkg name.
|
|
PINNED_KVER="$(echo '#{pkg}' | sed 's/^linux-image-//')"
|
|
if [ -f "/boot/vmlinuz-${PINNED_KVER}" ]; then
|
|
GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${PINNED_KVER}"
|
|
sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub
|
|
echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}"
|
|
update-grub 2>&1 | tail -3
|
|
fi
|
|
SHELL
|
|
end
|
|
|
|
# 2b. Pin via kernel.ubuntu.com/mainline/ if mainline_version is set.
|
|
# Fetches the four .debs (linux-headers _all, linux-headers _amd64
|
|
# generic, linux-image-unsigned generic, linux-modules generic),
|
|
# dpkg -i's them, regenerates grub, and prints a reboot hint.
|
|
# Mainline kernel package version like "5.15.5-051505" sorts ABOVE
|
|
# Ubuntu's stock "5.15.0-91" in debian-version-compare (numeric
|
|
# 51505 > 91), so update-grub puts it at boot index 0 and the next
|
|
# boot lands on it automatically.
|
|
if !mainline.empty?
|
|
m.vm.provision "shell", name: "pin-mainline-#{mainline}", inline: <<-SHELL
|
|
set -e
|
|
KVER="#{mainline}"
|
|
# already booted into it? Still fall through to grub-pin to
|
|
# make sure GRUB_DEFAULT stays correct even after stock kernel
|
|
# upgrades that might reorder grub entries.
|
|
BOOTED_INTO_TARGET=0
|
|
if uname -r | grep -q "^${KVER}-[0-9]\\+-generic"; then
|
|
echo "[=] mainline ${KVER} already booted ($(uname -r))"
|
|
BOOTED_INTO_TARGET=1
|
|
fi
|
|
|
|
# already installed on disk? Skip the download/install but
|
|
# still run the grub-pin block at the end.
|
|
SKIP_INSTALL=0
|
|
if ls /boot/vmlinuz-${KVER}-* >/dev/null 2>&1; then
|
|
echo "[=] mainline ${KVER} already installed on disk"
|
|
SKIP_INSTALL=1
|
|
fi
|
|
|
|
if [ "$SKIP_INSTALL" -eq 0 ]; then
|
|
echo "[+] fetching kernel.ubuntu.com mainline v${KVER}"
|
|
# Newer mainline kernels live under /v${KVER}/amd64/; older ones
|
|
# (≤ ~4.15) put debs at /v${KVER}/ directly. Try /amd64/ first;
|
|
# fall back to bare. linux-image-unsigned was renamed from
|
|
# linux-image- around 4.18 — old kernels use the plain name.
|
|
BASE="https://kernel.ubuntu.com/mainline/v${KVER}"
|
|
for URL in "${BASE}/amd64/" "${BASE}/"; do
|
|
INDEX=$(curl -sL "$URL")
|
|
if echo "$INDEX" | grep -q '\\.deb"'; then
|
|
break
|
|
fi
|
|
done
|
|
TMP=$(mktemp -d)
|
|
cd "$TMP"
|
|
# Pick the 4 canonical generic-kernel .debs by pattern match against
|
|
# the directory index. Skip lowlatency variants. Accept both
|
|
# 'linux-image-unsigned-' (newer) and 'linux-image-' (older).
|
|
DEBS=$(echo "$INDEX" | \\
|
|
grep -oE 'href="[^"]+\\.deb"' | sed 's/href="//; s/"$//' | \\
|
|
grep -E '(linux-image(-unsigned)?|linux-modules|linux-headers)-[0-9.]+-[0-9]+-generic_|linux-headers-[0-9.]+-[0-9]+_[^_]+_all\\.deb' | \\
|
|
grep -v lowlatency)
|
|
if [ -z "$DEBS" ]; then
|
|
echo "[-] no .debs found at ${BASE}/ (tried /amd64/ and bare)" >&2
|
|
exit 2
|
|
fi
|
|
for f in $DEBS; do
|
|
echo "[+] $f"
|
|
curl -fsSL -O "${URL}${f}"
|
|
done
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
dpkg -i *.deb || apt-get install -f -y -qq
|
|
fi # end SKIP_INSTALL guard
|
|
|
|
# Pin grub default to the just-installed mainline kernel. Without
|
|
# this, grub's debian-version-compare picks the highest-sorting
|
|
# vmlinuz-* as default; for downgrades (e.g. stock 4.15 → mainline
|
|
# 4.14.70), the OLD kernel wins because 4.15 > 4.14 numerically.
|
|
MAINLINE_VMLINUZ=$(ls /boot/vmlinuz-${KVER}-* 2>/dev/null | head -1)
|
|
if [ -n "$MAINLINE_VMLINUZ" ]; then
|
|
MAINLINE_KVER=$(basename "$MAINLINE_VMLINUZ" | sed 's/^vmlinuz-//')
|
|
# The "Advanced options" submenu entry id is stable across
|
|
# update-grub runs as "gnulinux-advanced-<UUID>>gnulinux-<kver>-advanced-<UUID>".
|
|
# Easier: use the human menuentry path.
|
|
GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${MAINLINE_KVER}"
|
|
sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub
|
|
echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}"
|
|
fi
|
|
update-grub 2>&1 | tail -3
|
|
echo "[i] mainline ${KVER} installed; reboot via 'vagrant reload'"
|
|
SHELL
|
|
end
|
|
|
|
# 2c. Optional per-module provisioner. If
|
|
# tools/verify-vm/provisioners/<module>.sh exists, run it as root
|
|
# before build-and-verify. Used for things only meaningful per-module:
|
|
# build sudo 1.9.16 from source (sudo_chwoot), drop a polkit allow
|
|
# rule (udisks_libblockdev), add a sudoers grant (sudo_runas_neg1).
|
|
skk_mod = ENV["SKK_MODULE"] || ""
|
|
if !skk_mod.empty?
|
|
prov_path = File.join(__dir__, "provisioners", "#{skk_mod}.sh")
|
|
if File.exist?(prov_path)
|
|
m.vm.provision "shell", name: "module-provision-#{skk_mod}",
|
|
path: prov_path
|
|
end
|
|
end
|
|
|
|
# 3. Build SKELETONKEY in-VM and run --explain --active for the target
|
|
# module. Runs as the unprivileged 'vagrant' user (NOT root) — most
|
|
# detect()s gate on "are you already root?" and short-circuit if so,
|
|
# which would invalidate every verification (pack2theroot was the
|
|
# motivating case). 'privileged: false' is how vagrant downshifts.
|
|
# SKK_MODULE is set by verify.sh on the second-pass `vagrant
|
|
# provision` call (post-reboot if kernel was pinned).
|
|
m.vm.provision "shell", name: "build-and-verify", run: "never",
|
|
privileged: false,
|
|
env: { "SKK_MODULE" => ENV["SKK_MODULE"] || "" },
|
|
inline: <<-SHELL
|
|
set -e
|
|
cd /vagrant
|
|
echo "[*] running as $(id)"
|
|
echo "[*] kernel: $(uname -r)"
|
|
echo "[*] building skeletonkey..."
|
|
make clean >/dev/null 2>&1 || true
|
|
make 2>&1 | tail -3
|
|
echo
|
|
echo "[*] running: skeletonkey --explain ${SKK_MODULE} --active"
|
|
echo
|
|
./skeletonkey --explain "${SKK_MODULE}" --active 2>&1 || true
|
|
SHELL
|
|
end
|
|
end
|