DF-3049 / verdict.json
{ "finding_id": "DF-3049", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "s_desc_size is only validated when the 64BIT feature is set; ext2_gd_csum() otherwise reads s_desc_size-32 bytes from each fs->e2fs_gd[i], past the allocation. Demonstrated via the console-printed crc: kernel 'expected' values (0x7c2e, 0x3c11) vary with heap state and differ from the exact in-bounds computation (0x04e5) -> live kernel heap bytes entered the crc. 16-bit-per-mount oracle; rw unmount writes the crc to the attacker device.", "exploit_chain": "crafted metadata_csum (no 64BIT) image, desc_size=512, gds 0..62 re-checksummed offline, gd63 wrong -> mount -> ext2_gd_csum_verify prints expected=0xNNNN computed over 448 OOB heap bytes -> repeat with varying desc_size / heap churn -> solve crc32c linear system for adjacent-heap bytes (KASLR-defeat / heap disclosure, ~N/2 mounts for N bytes)", "evidence": "console_lines.txt (3 mounts, varying expected), run.log, craft.py output (offline in-bounds csum 0x04e5 == stored), fix_run.txt (fixed: EINVAL, no bad-gd print)", "kernel_refs": ["sys/vfs/ext2fs/ext2_vfsops.c:549", "sys/vfs/ext2fs/ext2_csum.c:684", "sys/vfs/ext2fs/ext2_csum.c:713", "sys/vfs/ext2fs/ext2_csum.c:716", "sys/vfs/ext2fs/ext2_csum.c:733", "sys/vfs/ext2fs/ext2_vfsops.c:1570"], "poc_changes": "crafted from scratch: 64-group 1K-block metadata_csum fs (512MB sparse), desc_size=512, sb crc32c recomputed, gd csums backward-solved for desc_size=512 semantics so the kernel reaches group 57/63 before failing; crc32c implemented to match calculate_crc32c (no inversions).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 140, "guest_dirty": 0, "build_cmd": "python3 craft.py df3049.img ; cc -O2 -o /root/vntool /root/vntool.c", "run_cmd": "/root/vntool attach 0 /root/df3049.img; mount -t ext2fs -o ro /dev/vn0 /mnt/e2 (x3, with heap churn between)", "code_hash": "31924c7b19f16ce3319fed24739e5b3b7ea1502b643dd7bebff249d329126308", "notes": "first attempt failed at gd=56 due to a script-ordering bug (gd63 csum set after the backward solve) — fixed and re-run; the failure-at-56 artifact also confirmed the in-memory gd layout model. Leak rate limited (16 bits/mount) hence Medium despite certain OOB read.", "recommended_fix": "validate s_desc_size <= sizeof(struct ext2_gd) (E2FS_64BIT_GD_SIZE) in ext2_compute_sb_data() regardless of feature bits", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 (same kernel; ext2fs.ko rebuilt with fix.diff hunk 1, 2026-09-05)", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fixed module rejects the image with EINVAL at the desc_size bound before any gd checksum work; console shows no 'bad gd' line (no OOB read, no oracle).", "fix_evidence": "findings/poc/DF-3049/fix_run.txt; findings/poc/DF-3047/fix.diff (hunk 1)" } |