mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-29 18:20:58 +02:00
contrib: use LIEF for macOS security checks
This commit is contained in:
@ -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': [
|
||||||
|
Reference in New Issue
Block a user