contrib: use LIEF for PE security checks

This commit is contained in:
fanquake 2021-04-06 10:03:03 +08:00
parent a25b2e965c
commit 8e1f40dd9a
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1

View File

@ -6,22 +6,13 @@
Perform basic security checks on a series of executables. Perform basic security checks on a series of executables.
Exit status will be 0 if successful, and the program will be silent. Exit status will be 0 if successful, and the program will be silent.
Otherwise the exit status will be 1 and it will log which executables failed which checks. Otherwise the exit status will be 1 and it will log which executables failed which checks.
Needs `objdump` (for PE).
''' '''
import subprocess
import sys import sys
import os
from typing import List, Optional from typing import List, Optional
import lief import lief
import pixie import pixie
OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
def run_command(command) -> str:
p = subprocess.run(command, stdout=subprocess.PIPE, check=True, universal_newlines=True)
return p.stdout
def check_ELF_PIE(executable) -> bool: def check_ELF_PIE(executable) -> bool:
''' '''
Check for position independent executable (PIE), allowing for address space randomization. Check for position independent executable (PIE), allowing for address space randomization.
@ -143,46 +134,27 @@ def check_ELF_separate_code(executable):
return False return False
return True return True
def get_PE_dll_characteristics(executable) -> int:
'''Get PE DllCharacteristics bits'''
stdout = run_command([OBJDUMP_CMD, '-x', executable])
bits = 0
for line in stdout.splitlines():
tokens = line.split()
if len(tokens)>=2 and tokens[0] == 'DllCharacteristics':
bits = int(tokens[1],16)
return bits
IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100
def check_PE_DYNAMIC_BASE(executable) -> bool: def check_PE_DYNAMIC_BASE(executable) -> bool:
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
bits = get_PE_dll_characteristics(executable) binary = lief.parse(executable)
return (bits & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) == IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists
# Must support high-entropy 64-bit address space layout randomization # Must support high-entropy 64-bit address space layout randomization
# in addition to DYNAMIC_BASE to have secure ASLR. # in addition to DYNAMIC_BASE to have secure ASLR.
def check_PE_HIGH_ENTROPY_VA(executable) -> bool: def check_PE_HIGH_ENTROPY_VA(executable) -> bool:
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR''' '''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
bits = get_PE_dll_characteristics(executable) binary = lief.parse(executable)
return (bits & IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA) == IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists
def check_PE_RELOC_SECTION(executable) -> bool: def check_PE_RELOC_SECTION(executable) -> bool:
'''Check for a reloc section. This is required for functional ASLR.''' '''Check for a reloc section. This is required for functional ASLR.'''
stdout = run_command([OBJDUMP_CMD, '-h', executable]) binary = lief.parse(executable)
return binary.has_relocations
for line in stdout.splitlines():
if '.reloc' in line:
return True
return False
def check_PE_NX(executable) -> bool: def check_PE_NX(executable) -> bool:
'''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)''' '''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)'''
bits = get_PE_dll_characteristics(executable) binary = lief.parse(executable)
return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT return binary.has_nx
def check_MACHO_PIE(executable) -> bool: def check_MACHO_PIE(executable) -> bool:
''' '''