|
6 | 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 `objdump` (for PE) and `otool` (for MACHO). |
| 9 | +Needs `objdump` (for PE). |
10 | 10 | ''' |
11 | 11 | import subprocess |
12 | 12 | import sys |
13 | 13 | import os |
14 | 14 | from typing import List, Optional |
15 | 15 |
|
| 16 | +import lief |
16 | 17 | import pixie |
17 | 18 |
|
18 | 19 | OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') |
19 | | -OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool') |
20 | 20 |
|
21 | 21 | def run_command(command) -> str: |
22 | 22 | p = subprocess.run(command, stdout=subprocess.PIPE, check=True, universal_newlines=True) |
@@ -184,71 +184,41 @@ def check_PE_NX(executable) -> bool: |
184 | 184 | bits = get_PE_dll_characteristics(executable) |
185 | 185 | return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT |
186 | 186 |
|
187 | | -def get_MACHO_executable_flags(executable) -> List[str]: |
188 | | - stdout = run_command([OTOOL_CMD, '-vh', executable]) |
189 | | - |
190 | | - flags: List[str] = [] |
191 | | - for line in stdout.splitlines(): |
192 | | - tokens = line.split() |
193 | | - # filter first two header lines |
194 | | - if 'magic' in tokens or 'Mach' in tokens: |
195 | | - continue |
196 | | - # filter ncmds and sizeofcmds values |
197 | | - flags += [t for t in tokens if not t.isdigit()] |
198 | | - return flags |
199 | | - |
200 | 187 | def check_MACHO_PIE(executable) -> bool: |
201 | 188 | ''' |
202 | 189 | Check for position independent executable (PIE), allowing for address space randomization. |
203 | 190 | ''' |
204 | | - flags = get_MACHO_executable_flags(executable) |
205 | | - if 'PIE' in flags: |
206 | | - return True |
207 | | - return False |
| 191 | + binary = lief.parse(executable) |
| 192 | + return binary.is_pie |
208 | 193 |
|
209 | 194 | def check_MACHO_NOUNDEFS(executable) -> bool: |
210 | 195 | ''' |
211 | 196 | Check for no undefined references. |
212 | 197 | ''' |
213 | | - flags = get_MACHO_executable_flags(executable) |
214 | | - if 'NOUNDEFS' in flags: |
215 | | - return True |
216 | | - return False |
| 198 | + binary = lief.parse(executable) |
| 199 | + return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS) |
217 | 200 |
|
218 | 201 | def check_MACHO_NX(executable) -> bool: |
219 | 202 | ''' |
220 | 203 | Check for no stack execution |
221 | 204 | ''' |
222 | | - flags = get_MACHO_executable_flags(executable) |
223 | | - if 'ALLOW_STACK_EXECUTION' in flags: |
224 | | - return False |
225 | | - return True |
| 205 | + binary = lief.parse(executable) |
| 206 | + return binary.has_nx |
226 | 207 |
|
227 | 208 | def check_MACHO_LAZY_BINDINGS(executable) -> bool: |
228 | 209 | ''' |
229 | 210 | Check for no lazy bindings. |
230 | 211 | We don't use or check for MH_BINDATLOAD. See #18295. |
231 | 212 | ''' |
232 | | - stdout = run_command([OTOOL_CMD, '-l', executable]) |
233 | | - |
234 | | - for line in stdout.splitlines(): |
235 | | - tokens = line.split() |
236 | | - if 'lazy_bind_off' in tokens or 'lazy_bind_size' in tokens: |
237 | | - if tokens[1] != '0': |
238 | | - return False |
239 | | - return True |
| 213 | + binary = lief.parse(executable) |
| 214 | + return binary.dyld_info.lazy_bind == (0,0) |
240 | 215 |
|
241 | 216 | def check_MACHO_Canary(executable) -> bool: |
242 | 217 | ''' |
243 | 218 | Check for use of stack canary |
244 | 219 | ''' |
245 | | - stdout = run_command([OTOOL_CMD, '-Iv', executable]) |
246 | | - |
247 | | - ok = False |
248 | | - for line in stdout.splitlines(): |
249 | | - if '___stack_chk_fail' in line: |
250 | | - ok = True |
251 | | - return ok |
| 220 | + binary = lief.parse(executable) |
| 221 | + return binary.has_symbol('___stack_chk_fail') |
252 | 222 |
|
253 | 223 | CHECKS = { |
254 | 224 | 'ELF': [ |
|
0 commit comments