Skip to content

Commit 0655c7a

Browse files
committed
Merge #17787: scripts: add MACHO PIE check to security-check.py
7c9e821 scripts: add MACHO NOUNDEFS check to security-check.py (fanquake) 4ca92dc 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 7c9e821 Tree-SHA512: 5ba2f60440d0e31c70371a355c91ca4f723d80f7287d04e2098bf5b11892cc74216ff8f1454603c4db9675d4f7983614843b992b8dcfca0309aadf2aa7ab2e4b
2 parents 35fff5b + 7c9e821 commit 0655c7a

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

contrib/devtools/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ repository (requires pngcrush).
9898
security-check.py and test-security-check.py
9999
============================================
100100

101-
Perform basic ELF security checks on a series of executables.
101+
Perform basic security checks on a series of executables.
102102

103103
symbol-check.py
104104
===============

contrib/devtools/security-check.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
'''
6-
Perform basic ELF security checks on a series of executables.
6+
Perform basic security checks on a series of executables.
77
Exit status will be 0 if successful, and the program will be silent.
88
Otherwise the exit status will be 1 and it will log which executables failed which checks.
9-
Needs `readelf` (for ELF) and `objdump` (for PE).
9+
Needs `readelf` (for ELF), `objdump` (for PE) and `otool` (for MACHO).
1010
'''
1111
import subprocess
1212
import sys
1313
import os
1414

1515
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
1616
OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
17+
OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool')
1718
NONFATAL = {} # checks which are non-fatal for now but only generate a warning
1819

1920
def check_ELF_PIE(executable):
@@ -162,6 +163,40 @@ def check_PE_NX(executable):
162163
(arch,bits) = get_PE_dll_characteristics(executable)
163164
return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
164165

166+
def get_MACHO_executable_flags(executable):
167+
p = subprocess.Popen([OTOOL_CMD, '-vh', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
168+
(stdout, stderr) = p.communicate()
169+
if p.returncode:
170+
raise IOError('Error opening file')
171+
172+
flags = []
173+
for line in stdout.splitlines():
174+
tokens = line.split()
175+
# filter first two header lines
176+
if 'magic' in tokens or 'Mach' in tokens:
177+
continue
178+
# filter ncmds and sizeofcmds values
179+
flags += [t for t in tokens if not t.isdigit()]
180+
return flags
181+
182+
def check_MACHO_PIE(executable) -> bool:
183+
'''
184+
Check for position independent executable (PIE), allowing for address space randomization.
185+
'''
186+
flags = get_MACHO_executable_flags(executable)
187+
if 'PIE' in flags:
188+
return True
189+
return False
190+
191+
def check_MACHO_NOUNDEFS(executable) -> bool:
192+
'''
193+
Check for no undefined references.
194+
'''
195+
flags = get_MACHO_executable_flags(executable)
196+
if 'NOUNDEFS' in flags:
197+
return True
198+
return False
199+
165200
CHECKS = {
166201
'ELF': [
167202
('PIE', check_ELF_PIE),
@@ -173,6 +208,10 @@ def check_PE_NX(executable):
173208
('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),
174209
('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),
175210
('NX', check_PE_NX)
211+
],
212+
'MACHO': [
213+
('PIE', check_MACHO_PIE),
214+
('NOUNDEFS', check_MACHO_NOUNDEFS),
176215
]
177216
}
178217

@@ -183,6 +222,8 @@ def identify_executable(executable):
183222
return 'PE'
184223
elif magic.startswith(b'\x7fELF'):
185224
return 'ELF'
225+
elif magic.startswith(b'\xcf\xfa'):
226+
return 'MACHO'
186227
return None
187228

188229
if __name__ == '__main__':

contrib/gitian-descriptors/gitian-osx.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ script: |
137137
138138
CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS}
139139
make ${MAKEOPTS}
140+
make ${MAKEOPTS} -C src check-security
140141
make install-strip DESTDIR=${INSTALLPATH}
141142
142143
make osx_volname

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ endif
710710
check-security: $(bin_PROGRAMS)
711711
if HARDEN
712712
@echo "Checking binary security..."
713-
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
713+
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
714714
endif
715715

716716
if EMBEDDED_LEVELDB

0 commit comments

Comments
 (0)