patchelf icon indicating copy to clipboard operation
patchelf copied to clipboard

Segmentation fault with out of bounds sh_offset values

Open endofunky opened this issue 3 years ago • 0 comments

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

endofunky avatar Aug 21 '22 12:08 endofunky