README.md
Rendering markdown...
#!/bin/bash
# ─────────────────────────────────────────────────────────────────────
# KSMBD-012 Test Environment Setup
# ─────────────────────────────────────────────────────────────────────
#
# Builds a QEMU-bootable kernel (6.19.x) with ksmbd and an initramfs
# that auto-starts a vulnerable ksmbd server on boot.
#
# Current Ubuntu/Debian LTS kernels (<=6.11) do not include ksmbd
# durable handle support (merged in 6.12-rc1, commit c8efcc786146),
# so a QEMU environment is required.
#
# Prerequisites:
# sudo apt install build-essential flex bison bc libelf-dev libssl-dev \
# libglib2.0-dev libnl-3-dev libnl-genl-3-dev libtool autoconf \
# qemu-system-x86 busybox-static cpio python3-pip
# pip3 install impacket
#
# Usage:
# ./setup.sh # downloads kernel, builds everything
# ./setup.sh /path/to/source # use existing kernel source tree
#
# After setup completes, run:
# ./run.sh # boots QEMU, ksmbd starts automatically
# python3 exploit.py acl-bypass ... # from another terminal
# ─────────────────────────────────────────────────────────────────────
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
WORK="$SCRIPT_DIR/.build"
KERNEL_VER="${KERNEL_VER:-6.19.11}"
KSMBD_TOOLS_REPO="https://github.com/cifsd-team/ksmbd-tools.git"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GREEN}[*]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
err() { echo -e "${RED}[-]${NC} $*" >&2; exit 1; }
# ── Check prerequisites ───────────────────────────────────────────
for cmd in make gcc qemu-system-x86_64 cpio gzip git autoconf; do
command -v "$cmd" &>/dev/null || err "Missing: $cmd"
done
BUSYBOX="$(command -v busybox 2>/dev/null || true)"
[ -n "$BUSYBOX" ] && file "$BUSYBOX" | grep -q "statically linked" || \
err "Need statically-linked busybox (apt install busybox-static)"
mkdir -p "$WORK"
# ── Step 1: Kernel source ─────────────────────────────────────────
if [ -n "${1:-}" ] && [ -f "$1/Makefile" ]; then
KSRC="$(cd "$1" && pwd)"
info "Using existing kernel source: $KSRC"
else
KSRC="$WORK/linux-$KERNEL_VER"
if [ -f "$KSRC/Makefile" ]; then
info "Kernel source already present at $KSRC"
else
TARBALL="$WORK/linux-$KERNEL_VER.tar.xz"
if [ ! -f "$TARBALL" ]; then
MAJOR="${KERNEL_VER%%.*}"
URL="https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${KERNEL_VER}.tar.xz"
info "Downloading kernel $KERNEL_VER..."
curl -L -o "$TARBALL" "$URL"
fi
info "Extracting kernel source..."
tar -xf "$TARBALL" -C "$WORK"
fi
fi
# ── Step 2: Build ksmbd-tools ─────────────────────────────────────
TOOLS="$WORK/ksmbd-tools"
if [ ! -f "$TOOLS/tools/ksmbd.tools" ]; then
info "Building ksmbd-tools..."
[ -d "$TOOLS" ] || git clone --depth=1 "$KSMBD_TOOLS_REPO" "$TOOLS"
(cd "$TOOLS" && autoreconf -i && \
./configure --prefix=/ --sysconfdir=/etc --localstatedir=/var && \
make -j"$(nproc)")
fi
# Verify durable handle support
if ! strings "$TOOLS/tools/ksmbd.tools" | grep "durable handles" >/dev/null 2>&1; then
err "ksmbd-tools lacks durable handle support"
fi
info "ksmbd-tools ready ($(cd "$TOOLS" && ./tools/ksmbd.tools --version 2>&1 | head -1 || true))"
# ── Step 3: Configure and build kernel ─────────────────────────────
info "Configuring kernel..."
cd "$KSRC"
if [ ! -f .config ]; then
make defconfig
fi
scripts/config \
-e CONFIG_SMB_SERVER \
-e CONFIG_INET -e CONFIG_NET \
-e CONFIG_NLS -e CONFIG_NLS_UTF8 -e CONFIG_UNICODE \
-e CONFIG_CRYPTO_MD5 -e CONFIG_CRYPTO_HMAC -e CONFIG_CRYPTO_SHA256 \
-e CONFIG_CRYPTO_SHA512 -e CONFIG_CRYPTO_CMAC -e CONFIG_CRYPTO_AES \
-e CONFIG_CRYPTO_ECB -e CONFIG_CRYPTO_DES -e CONFIG_CRYPTO_CCM \
-e CONFIG_CRYPTO_GCM -e CONFIG_CRYPTO_CRC32 \
-e CONFIG_VIRTIO -e CONFIG_VIRTIO_NET -e CONFIG_VIRTIO_PCI \
-e CONFIG_DEVTMPFS -e CONFIG_DEVTMPFS_MOUNT -e CONFIG_TMPFS \
-e CONFIG_PROC_FS -e CONFIG_SERIAL_8250 -e CONFIG_SERIAL_8250_CONSOLE \
-e CONFIG_BLK_DEV_INITRD
make olddefconfig
grep -q "CONFIG_SMB_SERVER=y" .config || err "CONFIG_SMB_SERVER not enabled"
info "Building kernel ($(nproc) jobs)..."
make -j"$(nproc)" bzImage
BZIMAGE="$KSRC/arch/x86/boot/bzImage"
[ -f "$BZIMAGE" ] || err "bzImage not found"
# ── Step 4: Build initramfs ────────────────────────────────────────
info "Building initramfs..."
INITRD="$WORK/initramfs"
rm -rf "$INITRD"
mkdir -p "$INITRD"/{bin,sbin,lib,lib64,etc/ksmbd,proc,sys,dev,tmp/smbtest,run,var/run}
cp "$BUSYBOX" "$INITRD/bin/busybox"
(cd "$INITRD/bin" && for c in sh ls cat echo mkdir mount umount sleep ip ln \
mknod chmod chown kill rm grep; do ln -sf busybox "$c"; done)
cp "$TOOLS/tools/ksmbd.tools" "$INITRD/sbin/"
(cd "$INITRD/sbin" && ln -sf ksmbd.tools ksmbd.mountd && ln -sf ksmbd.tools ksmbd.adduser)
for lib in $(ldd "$TOOLS/tools/ksmbd.tools" 2>/dev/null | grep -o '/[^ ]*' | sort -u); do
[ -f "$lib" ] && { mkdir -p "$INITRD$(dirname "$lib")"; cp "$lib" "$INITRD$(dirname "$lib")/"; }
done
cp /lib64/ld-linux-x86-64.so.2 "$INITRD/lib64/" 2>/dev/null || true
# NSS plugin for getpwnam_r — dlopen()ed at runtime, not visible to ldd.
# Without this, ksmbd-tools cannot resolve victim/attacker → POSIX UIDs and
# every SMB user falls back to uid 0 (root), defeating the ACL boundary.
for libdir in /usr/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu /usr/lib64 /lib64; do
if [ -f "$libdir/libnss_files.so.2" ]; then
mkdir -p "$INITRD$libdir"
cp "$libdir/libnss_files.so.2" "$INITRD$libdir/"
break
fi
done
cat > "$INITRD/init" << 'INITSCRIPT'
#!/bin/sh
export PATH=/bin:/sbin
mount -t proc proc /proc; mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev; mount -t tmpfs tmpfs /tmp; mount -t tmpfs tmpfs /run
mknod -m 666 /dev/null c 1 3 2>/dev/null
ip link set lo up; ip addr add 127.0.0.1/8 dev lo
for iface in eth0 ens0; do
ip link set $iface up 2>/dev/null && { ip addr add 10.0.2.15/24 dev $iface; break; }
done
mkdir -p /tmp/smbtest /etc/ksmbd /var/run
# POSIX users — ksmbd-tools resolves SMB account → uid via getpwnam_r,
# so without these every SMB user maps to uid 0 and the ACL boundary
# we want to demonstrate disappears.
cat > /etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
victim:x:1000:1000:victim:/tmp:/bin/sh
attacker:x:1001:1001:attacker:/tmp:/bin/sh
EOF
cat > /etc/group << 'EOF'
root:x:0:
victim:x:1000:
attacker:x:1001:
EOF
cat > /etc/nsswitch.conf << 'EOF'
passwd: files
group: files
shadow: files
EOF
# Pre-stage the share with a victim-owned 0600 file so the ACL bypass
# is provable. Default file name matches `python3 exploit.py acl-bypass --file`.
# Share dir must be victim-writable: SMB DESIRED_ACCESS=DELETE on a file
# requires +w on the parent, otherwise victim's own CREATE is rejected.
chown 1000:1000 /tmp/smbtest
chmod 0700 /tmp/smbtest
echo 'placeholder - will be overwritten by victim' > /tmp/smbtest/secret_0600.txt
chown 1000:1000 /tmp/smbtest/secret_0600.txt
chmod 0600 /tmp/smbtest/secret_0600.txt
ksmbd.adduser -a -p Victim1 victim
ksmbd.adduser -a -p Attacker2 attacker
cat > /etc/ksmbd/ksmbd.conf << 'EOF'
[global]
server signing = disabled
smb3 encryption = disabled
server min protocol = SMB2_10
server max protocol = SMB3_11
# SMB2_10 = 2.1 (allows the dialect-regression test); SMB3_11 = 3.1.1.
durable handles = yes
[share]
path = /tmp/smbtest
read only = no
guest ok = no
valid users = victim, attacker
oplocks = yes
EOF
ksmbd.mountd -n &
sleep 2
echo ""
echo "=== ksmbd ready ==="
echo "Users: victim/Victim1 (uid 1000), attacker/Attacker2 (uid 1001)"
echo "Share: share -> /tmp/smbtest"
echo "Target: /tmp/smbtest/secret_0600.txt (0600 victim:victim)"
echo ""
exec /bin/sh
INITSCRIPT
chmod +x "$INITRD/init"
(cd "$INITRD" && find . | cpio -o -H newc 2>/dev/null | gzip > "$WORK/initramfs.cpio.gz")
# ── Step 5: Create run script ─────────────────────────────────────
cat > "$SCRIPT_DIR/run.sh" << RUNEOF
#!/bin/bash
# Boot the vulnerable ksmbd QEMU environment.
# Once you see "=== ksmbd ready ===" run from another terminal:
#
# python3 exploit.py acl-bypass \\
# --target 127.0.0.1 --port 44500 --share share \\
# --user victim --password Victim1 \\
# --user2 attacker --password2 Attacker2 \\
# --file secret_0600.txt
#
# Kill QEMU: Ctrl-A X
DIR="\$(cd "\$(dirname "\$0")" && pwd)"
exec qemu-system-x86_64 \\
-kernel "$BZIMAGE" \\
-initrd "$WORK/initramfs.cpio.gz" \\
-append "console=ttyS0 nokaslr nopti" \\
-m 512M -nographic -no-reboot \\
-netdev user,id=net0,hostfwd=tcp::\${PORT:-44500}-:445 \\
-device virtio-net-pci,netdev=net0
RUNEOF
chmod +x "$SCRIPT_DIR/run.sh"
# ── Done ───────────────────────────────────────────────────────────
echo ""
info "Setup complete."
info ""
info " Kernel: $BZIMAGE"
info " Initrd: $WORK/initramfs.cpio.gz"
info ""
info " Terminal 1: ./run.sh"
info " Terminal 2: python3 exploit.py acl-bypass \\"
info " --target 127.0.0.1 --port 44500 --share share \\"
info " --user victim --password Victim1 \\"
info " --user2 attacker --password2 Attacker2 \\"
info " --file secret_0600.txt"
info ""
info " Server-side check (in QEMU console after the exploit runs):"
info " ls -l /tmp/smbtest/secret_0600.txt"
info " cat /tmp/smbtest/secret_0600.txt"