3
3
# Distributed under the MIT software license, see the accompanying
4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
'''
6
- Perform basic ELF security checks on a series of executables.
6
+ Perform basic security checks on a series of executables.
7
7
Exit status will be 0 if successful, and the program will be silent.
8
8
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 ).
10
10
'''
11
11
import subprocess
12
12
import sys
13
13
import os
14
14
15
15
READELF_CMD = os .getenv ('READELF' , '/usr/bin/readelf' )
16
16
OBJDUMP_CMD = os .getenv ('OBJDUMP' , '/usr/bin/objdump' )
17
+ OTOOL_CMD = os .getenv ('OTOOL' , '/usr/bin/otool' )
17
18
NONFATAL = {} # checks which are non-fatal for now but only generate a warning
18
19
19
20
def check_ELF_PIE (executable ):
@@ -162,6 +163,31 @@ def check_PE_NX(executable):
162
163
(arch ,bits ) = get_PE_dll_characteristics (executable )
163
164
return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
164
165
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
+
165
191
CHECKS = {
166
192
'ELF' : [
167
193
('PIE' , check_ELF_PIE ),
@@ -173,6 +199,9 @@ def check_PE_NX(executable):
173
199
('DYNAMIC_BASE' , check_PE_DYNAMIC_BASE ),
174
200
('HIGH_ENTROPY_VA' , check_PE_HIGH_ENTROPY_VA ),
175
201
('NX' , check_PE_NX )
202
+ ],
203
+ 'MACHO' : [
204
+ ('PIE' , check_MACHO_PIE ),
176
205
]
177
206
}
178
207
@@ -183,6 +212,8 @@ def identify_executable(executable):
183
212
return 'PE'
184
213
elif magic .startswith (b'\x7f ELF' ):
185
214
return 'ELF'
215
+ elif magic .startswith (b'\xcf \xfa ' ):
216
+ return 'MACHO'
186
217
return None
187
218
188
219
if __name__ == '__main__' :
0 commit comments