예제 #1
0
def check_ELF_RELRO(executable) -> bool:
    '''
    Check for read-only relocations.
    GNU_RELRO program header must exist
    Dynamic section must have BIND_NOW flag
    '''
    elf = pixie.load(executable)
    have_gnu_relro = False
    for ph in elf.program_headers:
        # Note: not checking p_flags == PF_R: here as linkers set the permission differently
        # This does not affect security: the permission flags of the GNU_RELRO program
        # header are ignored, the PT_LOAD header determines the effective permissions.
        # However, the dynamic linker need to write to this area so these are RW.
        # Glibc itself takes care of mprotecting this area R after relocations are finished.
        # See also https://marc.info/?l=binutils&m=1498883354122353
        if ph.p_type == pixie.PT_GNU_RELRO:
            have_gnu_relro = True

    have_bindnow = False
    for flags in elf.query_dyn_tags(pixie.DT_FLAGS):
        assert isinstance(flags, int)
        if flags & pixie.DF_BIND_NOW:
            have_bindnow = True

    return have_gnu_relro and have_bindnow
예제 #2
0
def check_ELF_libraries(filename) -> bool:
    ok: bool = True
    elf = pixie.load(filename)
    for library_name in elf.query_dyn_tags(pixie.DT_NEEDED):
        assert(isinstance(library_name, bytes))
        if library_name.decode() not in ELF_ALLOWED_LIBRARIES:
            print('{}: NEEDED library {} is not allowed'.format(filename, library_name.decode()))
            ok = False
    return ok
예제 #3
0
def check_ELF_Canary(executable) -> bool:
    '''
    Check for use of stack canary
    '''
    elf = pixie.load(executable)
    ok = False
    for symbol in elf.dyn_symbols:
        if symbol.name == b'__stack_chk_fail':
            ok = True
    return ok
예제 #4
0
def check_exported_symbols(filename) -> bool:
    elf = pixie.load(filename)
    cppfilt = CPPFilt()
    ok: bool = True
    for symbol in elf.dyn_symbols:
        if not symbol.is_export:
            continue
        sym = symbol.name.decode()
        if elf.hdr.e_machine == pixie.EM_RISCV or sym in IGNORE_EXPORTS:
            continue
        print('{}: export of symbol {} not allowed'.format(filename, cppfilt(sym)))
        ok = False
    return ok
예제 #5
0
def check_ELF_NX(executable) -> bool:
    '''
    Check that no sections are writable and executable (including the stack)
    '''
    elf = pixie.load(executable)
    have_wx = False
    have_gnu_stack = False
    for ph in elf.program_headers:
        if ph.p_type == pixie.PT_GNU_STACK:
            have_gnu_stack = True
        if (ph.p_flags & pixie.PF_W) != 0 and (ph.p_flags & pixie.PF_X) != 0: # section is both writable and executable
            have_wx = True
    return have_gnu_stack and not have_wx
예제 #6
0
def check_imported_symbols(filename) -> bool:
    elf = pixie.load(filename)
    cppfilt = CPPFilt()
    ok: bool = True

    for symbol in elf.dyn_symbols:
        if not symbol.is_import:
            continue
        sym = symbol.name.decode()
        version = symbol.version.decode() if symbol.version is not None else None
        if version and not check_version(MAX_VERSIONS, version, elf.hdr.e_machine):
            print('{}: symbol {} from unsupported version {}'.format(filename, cppfilt(sym), version))
            ok = False
    return ok
예제 #7
0
def check_ELF_separate_code(executable):
    '''
    Check that sections are appropriately separated in virtual memory,
    based on their permissions. This checks for missing -Wl,-z,separate-code
    and potentially other problems.
    '''
    elf = pixie.load(executable)
    R = pixie.PF_R
    W = pixie.PF_W
    E = pixie.PF_X
    EXPECTED_FLAGS = {
        # Read + execute
        b'.init': R | E,
        b'.plt': R | E,
        b'.plt.got': R | E,
        b'.plt.sec': R | E,
        b'.text': R | E,
        b'.fini': R | E,
        # Read-only data
        b'.interp': R,
        b'.note.gnu.property': R,
        b'.note.gnu.build-id': R,
        b'.note.ABI-tag': R,
        b'.gnu.hash': R,
        b'.dynsym': R,
        b'.dynstr': R,
        b'.gnu.version': R,
        b'.gnu.version_r': R,
        b'.rela.dyn': R,
        b'.rela.plt': R,
        b'.rodata': R,
        b'.eh_frame_hdr': R,
        b'.eh_frame': R,
        b'.qtmetadata': R,
        b'.gcc_except_table': R,
        b'.stapsdt.base': R,
        # Writable data
        b'.init_array': R | W,
        b'.fini_array': R | W,
        b'.dynamic': R | W,
        b'.got': R | W,
        b'.data': R | W,
        b'.bss': R | W,
    }
    if elf.hdr.e_machine == pixie.EM_PPC64:
        # .plt is RW on ppc64 even with separate-code
        EXPECTED_FLAGS[b'.plt'] = R | W
    # For all LOAD program headers get mapping to the list of sections,
    # and for each section, remember the flags of the associated program header.
    flags_per_section = {}
    for ph in elf.program_headers:
        if ph.p_type == pixie.PT_LOAD:
            for section in ph.sections:
                assert(section.name not in flags_per_section)
                flags_per_section[section.name] = ph.p_flags
    # Spot-check ELF LOAD program header flags per section
    # If these sections exist, check them against the expected R/W/E flags
    for (section, flags) in flags_per_section.items():
        if section in EXPECTED_FLAGS:
            if EXPECTED_FLAGS[section] != flags:
                return False
    return True
예제 #8
0
def check_ELF_PIE(executable) -> bool:
    '''
    Check for position independent executable (PIE), allowing for address space randomization.
    '''
    elf = pixie.load(executable)
    return elf.hdr.e_type == pixie.ET_DYN