patchelf
patchelf copied to clipboard
Segmentation fault with out of bounds sh_offset values
Describe the bug
I was fuzzing patchelf with AFL++ this weekend and came across a few crashes that appear to be related to sh_offset in the section headers not being checked to be in bounds. When, for example, the .interp offset pounds beyond the end of file, this will cause patchelf to read from unallocated memory and cause a segmentation fault.
In gdb I could see that one place where this is happening is in getInterpreter():
In file: /home/ts/projects/sec/patchelf/patchelf/src/patchelf.cc
1099
1100 template<ElfFileParams>
1101 std::string ElfFile<ElfFileParamNames>::getInterpreter()
1102 {
1103 auto shdr = findSectionHeader(".interp");
► 1104 return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1);
1105 }
pwndbg> info locals
shdr = {
sh_name = <optimized out>,
sh_type = <optimized out>,
sh_flags = <optimized out>,
sh_addr = <optimized out>,
sh_offset = 8389120,
sh_size = <optimized out>,
sh_link = <optimized out>,
sh_info = <optimized out>,
sh_addralign = <optimized out>,
sh_entsize = <optimized out>
}
shdr = <optimized out>
Steps To Reproduce
Here is minimal example to reproduce this against the current master branch written in nasm assembly:
bits 32
ehdr:
;; ---------------------------------------------------------------------
;; Elf32_Ehdr: ELF header
;; ---------------------------------------------------------------------
db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident
times 8 db 0
dw 2 ; e_type
dw 3 ; e_machine
dd 1 ; e_version
dd 0 ; e_entry
dd 0 ; e_phoff
dd shdr ; e_shoff
dd 0 ; e_flags
dw ehdrsz ; e_ehsize
dw 0x20 ; e_phentsize
dw 0 ; e_phnum
dw 0x28 ; e_shentsize
dw 2 ; e_shnum
dw 0 ; e_shstrndx
ehdrsz: equ $ - ehdr
shdr:
;; ---------------------------------------------------------------------
;; Elf32_Shdr: String table
;; ---------------------------------------------------------------------
dd 0 ; sh_name
dd 3 ; sh_type
times 2 dd 0 ; sh_flags, sh_addr
dd s_tab ; sh_offset
dd s_tabsz ; sh_size
times 4 dd 0 ; sh_link, sh_info, sh_addralign, sh_entsize
;; ---------------------------------------------------------------------
;; Elf32_Shdr: Interpreter
;; ---------------------------------------------------------------------
dd s_interp - s_tab ; sh_name
dd 1 ; sh_type
times 2 dd 0 ; sh_flags, sh_addr
dd 0xFFFFFFFF ; sh_offset
times 5 dd 0 ; sh_size, sh_link, sh_info, sh_addralign,
; sh_entsize
s_tab:
;; ---------------------------------------------------------------------
;; String table
;; ---------------------------------------------------------------------
s_interp:
db '.interp', 0
s_tabsz: equ $ - s_tab
And here is the output from patchelf compiled with the address sanitizer enabled:
$ nasm -f bin crash.asm
$ ./patchelf --print-interpreter ./crash
AddressSanitizer:DEADLYSIGNAL
=================================================================
==162323==ERROR: AddressSanitizer: SEGV on unknown address 0x60d10000003f (pc 0x7f302878333d bp 0x7fff539ee140 sp 0x7fff539ed408 T0)
==162323==The signal is caused by a READ memory access.
#0 0x7f302878333d in __memcpy_sse2_unaligned (/nix/store/lxpdbaazqd2s79jx6lngr8nak2rjdaq1-glibc-2.34-210/lib/libc.so.6+0x9f33d)
#1 0x407d0c in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) [clone .isra.0] (/home/ef/projects/sec/patchelf/crash/patchelf+0x407d0c)
#2 0x427e29 in ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, unsigned int, unsigned int, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, unsigned short>::getInterpreter[abi:cxx11]() (/home/ef/projects/sec/patchelf/crash/patchelf+0x427e29)
#3 0x413ef6 in mainWrapped(int, char**) (/home/ef/projects/sec/patchelf/crash/patchelf+0x413ef6)
#4 0x405d15 in main (/home/ef/projects/sec/patchelf/crash/patchelf+0x405d15)
#5 0x7f302870d236 in __libc_start_call_main (/nix/store/lxpdbaazqd2s79jx6lngr8nak2rjdaq1-glibc-2.34-210/lib/libc.so.6+0x29236)
#6 0x7f302870d2f4 in __libc_start_main_impl (/nix/store/lxpdbaazqd2s79jx6lngr8nak2rjdaq1-glibc-2.34-210/lib/libc.so.6+0x292f4)
#7 0x4066a0 in _start (/home/ef/projects/sec/patchelf/crash/patchelf+0x4066a0)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/nix/store/lxpdbaazqd2s79jx6lngr8nak2rjdaq1-glibc-2.34-210/lib/libc.so.6+0x9f33d) in __memcpy_sse2_unaligned
==162323==ABORTING
Expected behavior
patchelf should print an error message when sections are out of bounds.
patchelf --version output
patchelf 0.15.0
Also on current master
Additional context
I've opened a PR with a potential fix: https://github.com/NixOS/patchelf/pull/398