diff options
author | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-01-02 12:44:32 +0100 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-01-02 12:48:49 +0100 |
commit | 0655c7a94cc9bf54d43eceed805e83f1b59e2409 (patch) | |
tree | 9b628192d148107c21d6e51ff3c088f0d9231369 | |
parent | 35fff5be60e853455abc24713481544e91adfedb (diff) | |
parent | 7c9e821c4e6cb186208ead9c8df616d1f393a49a (diff) |
Merge #17787: scripts: add MACHO PIE check to security-check.py
7c9e821c4e6cb186208ead9c8df616d1f393a49a scripts: add MACHO NOUNDEFS check to security-check.py (fanquake)
4ca92dc6d3f3e487d63286d8871d1829b3d279ff scripts: add MACHO PIE check to security-check.py (fanquake)
Pull request description:
This uses `otool -vh` to print the mach header and look for the `PIE` flag:
```bash
otool -vh src/bitcoind
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL LIB64 EXECUTE 24 2544 NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
```
From [`mach-o/loader.h`](https://opensource.apple.com/source/cctools/cctools-927.0.2/include/mach-o/loader.h.auto.html):
```c
#define MH_PIE 0x200000 /* When this bit is set, the OS will
load the main executable at a
random address. Only used in
MH_EXECUTE filetypes. */
```
ACKs for top commit:
laanwj:
code review ACK 7c9e821c4e6cb186208ead9c8df616d1f393a49a
Tree-SHA512: 5ba2f60440d0e31c70371a355c91ca4f723d80f7287d04e2098bf5b11892cc74216ff8f1454603c4db9675d4f7983614843b992b8dcfca0309aadf2aa7ab2e4b
-rw-r--r-- | contrib/devtools/README.md | 2 | ||||
-rwxr-xr-x | contrib/devtools/security-check.py | 45 | ||||
-rw-r--r-- | contrib/gitian-descriptors/gitian-osx.yml | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 |
4 files changed, 46 insertions, 4 deletions
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 04fa02484f..c35affac59 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -98,7 +98,7 @@ repository (requires pngcrush). security-check.py and test-security-check.py ============================================ -Perform basic ELF security checks on a series of executables. +Perform basic security checks on a series of executables. symbol-check.py =============== diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 44b7f6c7cc..9941c57479 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -3,10 +3,10 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' -Perform basic ELF 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. Otherwise the exit status will be 1 and it will log which executables failed which checks. -Needs `readelf` (for ELF) and `objdump` (for PE). +Needs `readelf` (for ELF), `objdump` (for PE) and `otool` (for MACHO). ''' import subprocess import sys @@ -14,6 +14,7 @@ import os READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') +OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool') NONFATAL = {} # checks which are non-fatal for now but only generate a warning def check_ELF_PIE(executable): @@ -162,6 +163,40 @@ def check_PE_NX(executable): (arch,bits) = get_PE_dll_characteristics(executable) return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT +def get_MACHO_executable_flags(executable): + p = subprocess.Popen([OTOOL_CMD, '-vh', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) + (stdout, stderr) = p.communicate() + if p.returncode: + raise IOError('Error opening file') + + flags = [] + 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 + +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 + CHECKS = { 'ELF': [ ('PIE', check_ELF_PIE), @@ -173,6 +208,10 @@ CHECKS = { ('DYNAMIC_BASE', check_PE_DYNAMIC_BASE), ('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA), ('NX', check_PE_NX) +], +'MACHO': [ + ('PIE', check_MACHO_PIE), + ('NOUNDEFS', check_MACHO_NOUNDEFS), ] } @@ -183,6 +222,8 @@ def identify_executable(executable): return 'PE' elif magic.startswith(b'\x7fELF'): return 'ELF' + elif magic.startswith(b'\xcf\xfa'): + return 'MACHO' return None if __name__ == '__main__': diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index d3a2645c53..75040c137f 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -137,6 +137,7 @@ script: | CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} make ${MAKEOPTS} + make ${MAKEOPTS} -C src check-security make install-strip DESTDIR=${INSTALLPATH} make osx_volname diff --git a/src/Makefile.am b/src/Makefile.am index 27c87688b4..b2736eed5b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -710,7 +710,7 @@ endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." - $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) + $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif if EMBEDDED_LEVELDB |