From 270ddc1681201f0a3fc211ce050c416cf61e1898 Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Sat, 23 May 2026 22:36:02 -0400 Subject: [PATCH] verify-vm: per-module provisioner hook + old-mainline URL fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds tools/verify-vm/provisioners/.sh hook so per-module setup (build vulnerable sudo from source, drop polkit allow rule, add sudoers grant) lives in checked-in scripts rather than manual VM steps. Vagrantfile runs the script as root before build-and-verify if it exists. Also fixes mainline kernel fetch to fall back from /v${KVER}/amd64/ to /v${KVER}/ for old kernels (≤ ~4.15) where debs aren't under the amd64 subdir, and accepts both 'linux-image-' (old) and 'linux-image-unsigned-' (new) deb names. Wires up 4 previously-deferred targets to expect VULNERABLE: - sudo_chwoot: builds sudo 1.9.16p1 from upstream into /usr/local - udisks_libblockdev: installs udisks2 + polkit rule for vagrant user - mutagen_astronomy: pins mainline 4.14.70 (one below the .71 fix) - sudo_runas_neg1: adds (ALL,!root) sudoers grant --- tools/verify-vm/Vagrantfile | 35 ++++++++++++++++--- tools/verify-vm/provisioners/sudo_chwoot.sh | 34 ++++++++++++++++++ .../verify-vm/provisioners/sudo_runas_neg1.sh | 16 +++++++++ .../provisioners/udisks_libblockdev.sh | 34 ++++++++++++++++++ tools/verify-vm/targets.yaml | 23 ++++++------ 5 files changed, 126 insertions(+), 16 deletions(-) create mode 100755 tools/verify-vm/provisioners/sudo_chwoot.sh create mode 100755 tools/verify-vm/provisioners/sudo_runas_neg1.sh create mode 100755 tools/verify-vm/provisioners/udisks_libblockdev.sh diff --git a/tools/verify-vm/Vagrantfile b/tools/verify-vm/Vagrantfile index 18f9420..04fb054 100644 --- a/tools/verify-vm/Vagrantfile +++ b/tools/verify-vm/Vagrantfile @@ -101,17 +101,28 @@ Vagrant.configure("2") do |c| exit 0 fi echo "[+] fetching kernel.ubuntu.com mainline v${KVER}" - URL="https://kernel.ubuntu.com/mainline/v${KVER}/amd64/" + # 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. - DEBS=$(curl -sL "$URL" | \\ + # 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 -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 $URL — does the version exist on kernel.ubuntu.com?" >&2 + echo "[-] no .debs found at ${BASE}/ (tried /amd64/ and bare)" >&2 exit 2 fi for f in $DEBS; do @@ -125,6 +136,20 @@ Vagrant.configure("2") do |c| SHELL end + # 2c. Optional per-module provisioner. If + # tools/verify-vm/provisioners/.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, diff --git a/tools/verify-vm/provisioners/sudo_chwoot.sh b/tools/verify-vm/provisioners/sudo_chwoot.sh new file mode 100755 index 0000000..a832bab --- /dev/null +++ b/tools/verify-vm/provisioners/sudo_chwoot.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# CVE-2025-32463 sudo --chroot NSS injection (Stratascale). Vulnerable +# range is sudo [1.9.14, 1.9.17p0]. Ubuntu 22.04 ships 1.9.9 which +# PREDATES the --chroot code path. Build sudo 1.9.16p1 from upstream +# and install to /usr/local (which precedes /usr/bin in Ubuntu's default +# PATH so plain `sudo` resolves to the vulnerable binary). +set -e + +export DEBIAN_FRONTEND=noninteractive +apt-get install -y -qq libpam0g-dev libssl-dev wget make gcc >/dev/null + +cd /tmp +TARBALL=sudo-1.9.16p1.tar.gz +URL="https://www.sudo.ws/dist/${TARBALL}" + +if [ -x /usr/local/bin/sudo ] && /usr/local/bin/sudo --version 2>&1 | head -1 | grep -q "1.9.16p1"; then + echo "[=] sudo 1.9.16p1 already at /usr/local/bin/sudo" +else + [ -f "${TARBALL}" ] || wget -q "${URL}" + rm -rf sudo-1.9.16p1 + tar xzf "${TARBALL}" + cd sudo-1.9.16p1 + # --sysconfdir=/etc so it honors the existing /etc/sudoers (vagrant's + # NOPASSWD grant). --disable-shared keeps the build self-contained. + ./configure --prefix=/usr/local --sysconfdir=/etc \ + --disable-shared --quiet >/dev/null 2>&1 + make -j"$(nproc)" >/tmp/sudo-build.log 2>&1 || { tail -40 /tmp/sudo-build.log; exit 1; } + make install >/tmp/sudo-install.log 2>&1 || { tail -40 /tmp/sudo-install.log; exit 1; } +fi + +# Verify what the unprivileged user's PATH resolves to. +echo "[+] which sudo (root): $(which sudo)" +echo "[+] /usr/local/bin/sudo version: $(/usr/local/bin/sudo --version | head -1)" +sudo -u vagrant bash -c 'echo "[+] vagrant PATH: $PATH"; echo "[+] vagrant sees: $(which sudo)"; sudo --version | head -1' diff --git a/tools/verify-vm/provisioners/sudo_runas_neg1.sh b/tools/verify-vm/provisioners/sudo_runas_neg1.sh new file mode 100755 index 0000000..78a0053 --- /dev/null +++ b/tools/verify-vm/provisioners/sudo_runas_neg1.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# CVE-2019-14287 needs a (ALL,!root) grant for find_runas_blacklist_grant() +# to fire. Ubuntu 18.04 ships sudo 1.8.21p2 (in the vulnerable range) but +# Vagrant's default sudoers doesn't include the grant. Add it. +set -e + +cat >/etc/sudoers.d/99-skk-runas-neg1 <<'EOF' +vagrant ALL=(ALL,!root) NOPASSWD: /bin/vi +EOF +chmod 0440 /etc/sudoers.d/99-skk-runas-neg1 + +echo "[+] sudoers grant installed:" +grep . /etc/sudoers.d/99-skk-runas-neg1 +echo +echo "[+] sudo -ln -U vagrant tail:" +sudo -ln -U vagrant 2>&1 | tail -10 || true diff --git a/tools/verify-vm/provisioners/udisks_libblockdev.sh b/tools/verify-vm/provisioners/udisks_libblockdev.sh new file mode 100755 index 0000000..38d5b25 --- /dev/null +++ b/tools/verify-vm/provisioners/udisks_libblockdev.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# CVE-2025-6019 udisks/libblockdev SUID-on-mount (Qualys). Debian 12's +# cloud image is server-oriented and doesn't ship udisks2. Install it, +# and drop a polkit rule allowing the vagrant user to invoke the +# affected action.ids — the real-world bug-path is "active console +# user invokes loop-setup", and we don't have a graphical session in +# Vagrant. The polkit rule simulates the trust polkit would give a +# logged-in workstation user. +set -e + +export DEBIAN_FRONTEND=noninteractive +apt-get install -y -qq udisks2 libblockdev-utils3 >/dev/null + +mkdir -p /etc/polkit-1/rules.d +cat >/etc/polkit-1/rules.d/49-skk-verify.rules <<'EOF' +polkit.addRule(function(action, subject) { + if (subject.user == "vagrant" && + (action.id == "org.freedesktop.UDisks2.loop-setup" || + action.id == "org.freedesktop.UDisks2.filesystem-mount" || + action.id == "org.freedesktop.UDisks2.filesystem-mount-other-seat" || + action.id == "org.freedesktop.UDisks2.modify-device")) { + return polkit.Result.YES; + } +}); +EOF + +systemctl enable udisks2.service >/dev/null 2>&1 || true +systemctl restart udisks2.service +sleep 2 + +echo "[+] udisks2 status:" +systemctl is-active udisks2.service +echo "[+] udisks2 version: $(dpkg-query -W -f='${Version}' udisks2)" +echo "[+] libblockdev version: $(dpkg-query -W -f='${Version}' libblockdev-utils3 2>/dev/null || dpkg-query -W -f='${Version}' libblockdev-utils2)" diff --git a/tools/verify-vm/targets.yaml b/tools/verify-vm/targets.yaml index cdf51f6..07caca3 100644 --- a/tools/verify-vm/targets.yaml +++ b/tools/verify-vm/targets.yaml @@ -224,18 +224,18 @@ vmwgfx: # ── v0.8.0 additions ────────────────────────────────────────────── sudo_chwoot: - box: ubuntu2204 # 22.04 ships sudo 1.9.9 (pre-feature) — need a 1.9.14+ install + box: ubuntu2204 # 22.04 ships sudo 1.9.9 — provisioner builds 1.9.16p1 over it kernel_pkg: "" # this bug is sudo-version-gated, not kernel kernel_version: "5.15.0" - expect_detect: OK - notes: "CVE-2025-32463; sudo --chroot NSS shim. Vulnerable range is sudo [1.9.14, 1.9.17p0]. Ubuntu 22.04 ships sudo 1.9.9 which PREDATES the vulnerable --chroot code path — so detect correctly returns OK. To validate VULNERABLE empirically, provision a vulnerable sudo build into the VM (e.g. apt install -t backports sudo=1.9.16-1 or build from source). Deferred." + expect_detect: VULNERABLE + notes: "CVE-2025-32463; sudo --chroot NSS shim. Vulnerable range is sudo [1.9.14, 1.9.17p0]. provisioners/sudo_chwoot.sh builds sudo 1.9.16p1 from upstream sources into /usr/local/bin (which precedes /usr/bin in PATH so plain `sudo` resolves to the vulnerable binary)." udisks_libblockdev: box: debian12 # 12 ships udisks2 2.10.x + libblockdev 3.0.x — vulnerable kernel_pkg: "" kernel_version: "6.1.0" - expect_detect: PRECOND_FAIL - notes: "CVE-2025-6019; udisks/libblockdev SUID-on-mount. Debian 12's cloud image is server-oriented — udisksd is NOT installed by default. detect correctly returns PRECOND_FAIL ('udisksd not installed; bug unreachable here'). To validate VULNERABLE empirically, install udisks2 + log in as an active-session user (Vagrant SSH session is NOT active per polkit — needs a real console session). Both gates are real and the detect honestly surfaces them; deferred." + expect_detect: VULNERABLE + notes: "CVE-2025-6019; udisks/libblockdev SUID-on-mount. provisioners/udisks_libblockdev.sh installs udisks2 + libblockdev-utils3 and drops a polkit rule allowing the vagrant user to invoke loop-setup/filesystem-mount — simulating the trust polkit would give a logged-in workstation user (the real-world bug-path). Without that rule, the SSH session is not 'active' per polkit and the D-Bus call short-circuits." pintheft: box: "" # RDS is blacklisted on every common Vagrant box's stock kernel @@ -248,18 +248,19 @@ pintheft: # ── v0.9.0 additions (gap fillers 2018 / 2019 / 2020 / 2024) ────── mutagen_astronomy: - box: ubuntu1804 # 4.15.0-213 stock — already > 4.14.71 backport → OK + box: ubuntu1804 # stock 4.15.0-213 is post-fix; mainline 4.14.70 is one below the .71 fix kernel_pkg: "" - kernel_version: "4.15.0" - expect_detect: OK - notes: "CVE-2018-14634; Qualys Mutagen Astronomy. Ubuntu 18.04 ships 4.15.0-213 which is post-fix. detect correctly returns OK. Verifying the VULNERABLE path empirically needs a 2.6.x / 3.10.x EOL kernel (e.g. RHEL 6 / CentOS 6 / Debian 7); deferred to a custom-box workflow." + mainline_version: "4.14.70" + kernel_version: "4.14.70" + expect_detect: VULNERABLE + notes: "CVE-2018-14634; Qualys Mutagen Astronomy. Mainline 4.14.70 sits one stable below the 4.14.71 backport. Old mainline kernels live at /v${KVER}/ directly (no /amd64/ subdir); Vagrantfile's pin-mainline provisioner falls back to the bare path." sudo_runas_neg1: box: ubuntu1804 # ships sudo 1.8.21p2 (vulnerable; pre-1.8.28 fix) kernel_pkg: "" kernel_version: "4.15.0" - expect_detect: PRECOND_FAIL - notes: "CVE-2019-14287; sudo Runas -u#-1. Ubuntu 18.04 ships sudo 1.8.21p2 which IS in the vulnerable range — but the default vagrant user has no (ALL,!root) sudoers grant for find_runas_blacklist_grant() to abuse, so detect correctly returns PRECOND_FAIL. To validate VULNERABLE empirically, provision a sudoers entry of the form 'vagrant ALL=(ALL,!root) /bin/vi' before verifying." + expect_detect: VULNERABLE + notes: "CVE-2019-14287; sudo Runas -u#-1. Ubuntu 18.04 ships sudo 1.8.21p2 (vulnerable). provisioners/sudo_runas_neg1.sh adds 'vagrant ALL=(ALL,!root) NOPASSWD: /bin/vi' to /etc/sudoers.d/ so find_runas_blacklist_grant() has a grant to abuse." tioscpgrp: box: ubuntu2004 # 5.4 stock kernels (5.4.0-26) are below the 5.4.85 backport