file/libmagic — OOB Read in ELF Core Note Parser
A missing bounds check in file's ELF core note parser. When processing a FreeBSD-style NT_PRPSINFO note, the code reads the process name field without first verifying that the field's offset falls within the note's data buffer. A crafted ELF core file with a small NT_PRPSINFO descriptor triggers an out-of-bounds read.
1. Context
file(1) and libmagic parse ELF core files to extract identifying information — the process name, PID, and signal. The relevant code is in src/readelf.c, in the do_core_note() function. I audited this file while looking at how file handles the note section of ELF files, specifically the paths that deal with different OS-specific note formats.
2. NT_PRPSINFO and the FreeBSD layout
NT_PRPSINFO is an ELF note type that stores process information in a core dump. The in-memory layout differs between OS families. In the FreeBSD variant, the process name (a null-terminated string, up to 80 characters) sits at a fixed offset from the start of the note descriptor, with the offset depending on whether the core is 32-bit or 64-bit:
if (clazz == ELFCLASS32)
argoff = 4 + 4 + 17; /* = 25 */
else
argoff = 4 + 4 + 8 + 17; /* = 33 */
The code then reads the process name directly:
if (elf_printf(ms, ", from '%.80s'", nbuf + doff + argoff) == -1)
return -1;
3. The missing check
The read at nbuf + doff + argoff happens before any check that doff + argoff + 81 (the maximum number of bytes the %.80s format could read, plus the null terminator scan) is within the bounds of the note's data buffer. The note descriptor is attacker-controlled: a crafted ELF core file can set descsz to a small value while still providing a valid-looking NT_PRPSINFO note type, making doff + argoff point past the end of the allocated buffer.
There is a size check one line later for the PID field:
pidoff = argoff + 81 + 2;
if (doff + pidoff + 4 <= size) {
/* read PID */
}
So the PID access is guarded, but the process name access immediately before it is not.
4. Triggering it
A minimal ELF core file with a PT_NOTE segment containing an NT_PRPSINFO note and a descsz smaller than argoff + 81 is enough. When file crafted.core runs, do_core_note() enters the OS_STYLE_FREEBSD branch, computes argoff, and calls elf_printf with a pointer past the note's data. Depending on what follows in memory, this reads whatever happens to be there — adjacent heap data or mapped pages — until a null byte is found.
5. Fix — commit 6bb1b445
Christos Zoulas committed the fix in commit 6bb1b445 with the message "Add missing bounds check (Alexandru Hossu)". The change adds the missing guard before the process name read:
+if (doff + argoff + 81 <= size) {
if (elf_printf(ms, ", from '%.80s'", nbuf + doff + argoff) == -1)
return -1;
+}