DF-4184 / verdict.json
{ "finding_id": "DF-4184", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "acd_fixate() sizes its MODE SELECT data-out transfer from the device-fed write-parameters page_length (offset 9 of the MODE SENSE 0x05 response) with no validation, sending page_length+10 bytes out of a 64-byte __packed stack struct: a crafted ATAPI device answering page_length=0xff receives 201 bytes of adjacent kernel stack. Reproduced on the audit guest using the DF-3690-family crafted-device emulation (guest kernel rebuilt with a device-response hook in ata_atapicmd; atapi-cd.c unmodified): MODE SELECT count=265 observed, and the over-read payload contains 0xfffff800xxxxxxxx kernel pointers and 0xffffffff80xxxxxx kernel-text pointers (KASLR-defeating material). Reachability on stock DF is gated by the SYSCAP_RESTRICTEDROOT disk-open check (subr_disk.c:1072), so the realistic pairing is root/RESTRICTEDROOT burning software against a malicious or firmware-compromised drive; the device-controlled input must not be trusted regardless.", "exploit_chain": "crafted ATAPI device answers MODE SENSE(0x05) with page_length=0xff -> kernel CDRIOCFIXATE -> acd_fixate -> acd_mode_select(¶m, 0xff+10) -> 265-byte DATA-OUT from 64-byte stack object -> device harvests 201 bytes of kernel stack incl. kernel text/heap/stack pointers (KASLR defeat / target reconnaissance for a follow-up memory-corruption exploit)", "evidence": "run.log (decisive run: hook ccb0=55 ... cnt=265 + over-read hexdump with annotated kernel pointers); leak_sample.txt (two independent samples); dfaud_dmesg_raw.txt; fix_run.log + fix.2.log (fix validation: count=60 in-bounds / EINVAL with no data-out); VERDICT.md", "kernel_refs": [ "sys/dev/disk/nata/atapi-cd.c:1140", "sys/dev/disk/nata/atapi-cd.c:1150", "sys/dev/disk/nata/atapi-cd.c:1674", "sys/dev/disk/nata/ata-queue.c:188", "sys/dev/disk/nata/atapi-cd.h:84" ], "poc_changes": "initial draft passed (void*)1 as the ioctl arg (EFAULT at copyin); fixed to a real int pointer; added TOC/blocksize sanity probes; verification required a guest-local crafted-device emulation hook (emu_notes.txt) because stock QEMU ATAPI refuses MODE SENSE page 0x05 (verified: ILLEGAL REQUEST asc=0x24)", "attempts": 6, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0..#6 x86_64 (audit guest, QEMU 7.2, /dev/acd0 = QEMU DVD-ROM with inserted 16MiB raw medium)", "runtime_sec": 1500, "guest_dirty": 1, "build_cmd": "guest: cd /tmp && cc -O -o fixate_stackread fixate_stackread.c ; kernel: cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC", "run_cmd": "/tmp/fixate_stackread /dev/acd0 (tunables: df_audit.mode=1 [df_audit.capfail=1 df_audit.pl=255|50])", "code_hash": "e1a688ff932d16a09c22db3c127211b4126b6b787c833508723f9395036976de", "notes": "sizeof(struct write_param) computed by the guest kernel = 64 bytes (hook printed cnt=64 for the mode sense); over-read = 265-64 = 201 bytes. Unprivileged reachability is additionally blocked by the disk-layer RESTRICTEDROOT gate (tested: operator-group user gets EPERM on open).", "recommended_fix": "clamp the device-fed page length in acd_fixate to the MMC maximum (1..0x32) before acd_mode_select", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied on top of the same crafted-device emulation and rebuilt: with the identical attack (page_length=0xff) acd_fixate returns EINVAL and issues no MODE SELECT at all (no data-out, no disclosure); with a valid page (0x32) MODE SELECT is sized 60 bytes (in-bounds) and the driver completes the ioctl without the trap that the same configuration caused pre-fix (fix.2.log). Guest kernel #6 (uname #6: Fri Sep 18 12:49:47 UTC 2026)", "fix_evidence": "fix_run.log: CDRIOCFIXATE -> ret=-1 errno=22 and NO ccb0=55 line; fix.2.log: hook ccb0=55 ccb2=00 cnt=60 (was 265) + survived (no panic) with capfail=1 (baseline: Fatal trap 18 idivl panic)", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #6: Fri Sep 18 12:49:47 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64" } |