contrib: use LIEF for macOS security checks

This commit is contained in:
fanquake
2021-04-07 09:38:00 +08:00
parent 7e7eae7aa8
commit a25b2e965c

View File

@ -6,17 +6,17 @@
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) and `otool` (for MACHO). Needs `objdump` (for PE).
''' '''
import subprocess import subprocess
import sys import sys
import os import os
from typing import List, Optional from typing import List, Optional
import lief
import pixie import pixie
OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool')
def run_command(command) -> str: def run_command(command) -> str:
p = subprocess.run(command, stdout=subprocess.PIPE, check=True, universal_newlines=True) 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) bits = get_PE_dll_characteristics(executable)
return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 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: def check_MACHO_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.
''' '''
flags = get_MACHO_executable_flags(executable) binary = lief.parse(executable)
if 'PIE' in flags: return binary.is_pie
return True
return False
def check_MACHO_NOUNDEFS(executable) -> bool: def check_MACHO_NOUNDEFS(executable) -> bool:
''' '''
Check for no undefined references. Check for no undefined references.
''' '''
flags = get_MACHO_executable_flags(executable) binary = lief.parse(executable)
if 'NOUNDEFS' in flags: return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS)
return True
return False
def check_MACHO_NX(executable) -> bool: def check_MACHO_NX(executable) -> bool:
''' '''
Check for no stack execution Check for no stack execution
''' '''
flags = get_MACHO_executable_flags(executable) binary = lief.parse(executable)
if 'ALLOW_STACK_EXECUTION' in flags: return binary.has_nx
return False
return True
def check_MACHO_LAZY_BINDINGS(executable) -> bool: def check_MACHO_LAZY_BINDINGS(executable) -> bool:
''' '''
Check for no lazy bindings. Check for no lazy bindings.
We don't use or check for MH_BINDATLOAD. See #18295. We don't use or check for MH_BINDATLOAD. See #18295.
''' '''
stdout = run_command([OTOOL_CMD, '-l', executable]) binary = lief.parse(executable)
return binary.dyld_info.lazy_bind == (0,0)
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
def check_MACHO_Canary(executable) -> bool: def check_MACHO_Canary(executable) -> bool:
''' '''
Check for use of stack canary Check for use of stack canary
''' '''
stdout = run_command([OTOOL_CMD, '-Iv', executable]) binary = lief.parse(executable)
return binary.has_symbol('___stack_chk_fail')
ok = False
for line in stdout.splitlines():
if '___stack_chk_fail' in line:
ok = True
return ok
CHECKS = { CHECKS = {
'ELF': [ 'ELF': [