From a25b2e965c93fe2a46a2f8f1e7bdf5642d453511 Mon Sep 17 00:00:00 2001 From: fanquake Date: Wed, 7 Apr 2021 09:38:00 +0800 Subject: contrib: use LIEF for macOS security checks --- contrib/devtools/security-check.py | 54 +++++++++----------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 7a0761ad15..bc65d9a9be 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -6,17 +6,17 @@ Perform basic security checks on a series of executables. 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. -Needs `objdump` (for PE) and `otool` (for MACHO). +Needs `objdump` (for PE). ''' import subprocess import sys import os from typing import List, Optional +import lief import pixie OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') -OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool') def run_command(command) -> str: p = subprocess.run(command, stdout=subprocess.PIPE, check=True, universal_newlines=True) @@ -184,71 +184,41 @@ def check_PE_NX(executable) -> bool: bits = get_PE_dll_characteristics(executable) return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT -def get_MACHO_executable_flags(executable) -> List[str]: - stdout = run_command([OTOOL_CMD, '-vh', executable]) - - flags: List[str] = [] - for line in stdout.splitlines(): - tokens = line.split() - # filter first two header lines - if 'magic' in tokens or 'Mach' in tokens: - continue - # filter ncmds and sizeofcmds values - flags += [t for t in tokens if not t.isdigit()] - return flags - def check_MACHO_PIE(executable) -> bool: ''' Check for position independent executable (PIE), allowing for address space randomization. ''' - flags = get_MACHO_executable_flags(executable) - if 'PIE' in flags: - return True - return False + binary = lief.parse(executable) + return binary.is_pie def check_MACHO_NOUNDEFS(executable) -> bool: ''' Check for no undefined references. ''' - flags = get_MACHO_executable_flags(executable) - if 'NOUNDEFS' in flags: - return True - return False + binary = lief.parse(executable) + return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS) def check_MACHO_NX(executable) -> bool: ''' Check for no stack execution ''' - flags = get_MACHO_executable_flags(executable) - if 'ALLOW_STACK_EXECUTION' in flags: - return False - return True + binary = lief.parse(executable) + return binary.has_nx def check_MACHO_LAZY_BINDINGS(executable) -> bool: ''' Check for no lazy bindings. We don't use or check for MH_BINDATLOAD. See #18295. ''' - stdout = run_command([OTOOL_CMD, '-l', executable]) - - for line in stdout.splitlines(): - tokens = line.split() - if 'lazy_bind_off' in tokens or 'lazy_bind_size' in tokens: - if tokens[1] != '0': - return False - return True + binary = lief.parse(executable) + return binary.dyld_info.lazy_bind == (0,0) def check_MACHO_Canary(executable) -> bool: ''' Check for use of stack canary ''' - stdout = run_command([OTOOL_CMD, '-Iv', executable]) - - ok = False - for line in stdout.splitlines(): - if '___stack_chk_fail' in line: - ok = True - return ok + binary = lief.parse(executable) + return binary.has_symbol('___stack_chk_fail') CHECKS = { 'ELF': [ -- cgit v1.2.3