aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/devtools/security-check.py69
-rwxr-xr-xcontrib/devtools/symbol-check.py26
-rwxr-xr-xcontrib/devtools/test-security-check.py97
3 files changed, 73 insertions, 119 deletions
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index f57e9abfec..46f9ee915f 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -38,13 +38,13 @@ def check_ELF_RELRO(binary) -> bool:
return have_gnu_relro and have_bindnow
-def check_ELF_Canary(binary) -> bool:
+def check_ELF_CANARY(binary) -> bool:
'''
Check for use of stack canary
'''
return binary.has_symbol('__stack_chk_fail')
-def check_ELF_separate_code(binary):
+def check_ELF_SEPARATE_CODE(binary):
'''
Check that sections are appropriately separated in virtual memory,
based on their permissions. This checks for missing -Wl,-z,separate-code
@@ -105,7 +105,7 @@ def check_ELF_separate_code(binary):
return False
return True
-def check_ELF_control_flow(binary) -> bool:
+def check_ELF_CONTROL_FLOW(binary) -> bool:
'''
Check for control flow instrumentation
'''
@@ -130,7 +130,7 @@ def check_PE_RELOC_SECTION(binary) -> bool:
'''Check for a reloc section. This is required for functional ASLR.'''
return binary.has_relocations
-def check_PE_control_flow(binary) -> bool:
+def check_PE_CONTROL_FLOW(binary) -> bool:
'''
Check for control flow instrumentation
'''
@@ -145,7 +145,7 @@ def check_PE_control_flow(binary) -> bool:
return True
return False
-def check_PE_Canary(binary) -> bool:
+def check_PE_CANARY(binary) -> bool:
'''
Check for use of stack canary
'''
@@ -163,7 +163,7 @@ def check_MACHO_FIXUP_CHAINS(binary) -> bool:
'''
return binary.has_dyld_chained_fixups
-def check_MACHO_Canary(binary) -> bool:
+def check_MACHO_CANARY(binary) -> bool:
'''
Check for use of stack canary
'''
@@ -182,7 +182,7 @@ def check_NX(binary) -> bool:
'''
return binary.has_nx
-def check_MACHO_control_flow(binary) -> bool:
+def check_MACHO_CONTROL_FLOW(binary) -> bool:
'''
Check for control flow instrumentation
'''
@@ -192,7 +192,7 @@ def check_MACHO_control_flow(binary) -> bool:
return True
return False
-def check_MACHO_branch_protection(binary) -> bool:
+def check_MACHO_BRANCH_PROTECTION(binary) -> bool:
'''
Check for branch protection instrumentation
'''
@@ -206,8 +206,8 @@ BASE_ELF = [
('PIE', check_PIE),
('NX', check_NX),
('RELRO', check_ELF_RELRO),
- ('Canary', check_ELF_Canary),
- ('separate_code', check_ELF_separate_code),
+ ('CANARY', check_ELF_CANARY),
+ ('SEPARATE_CODE', check_ELF_SEPARATE_CODE),
]
BASE_PE = [
@@ -216,19 +216,19 @@ BASE_PE = [
('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),
('NX', check_NX),
('RELOC_SECTION', check_PE_RELOC_SECTION),
- ('CONTROL_FLOW', check_PE_control_flow),
- ('Canary', check_PE_Canary),
+ ('CONTROL_FLOW', check_PE_CONTROL_FLOW),
+ ('CANARY', check_PE_CANARY),
]
BASE_MACHO = [
('NOUNDEFS', check_MACHO_NOUNDEFS),
- ('Canary', check_MACHO_Canary),
+ ('CANARY', check_MACHO_CANARY),
('FIXUP_CHAINS', check_MACHO_FIXUP_CHAINS),
]
CHECKS = {
lief.EXE_FORMATS.ELF: {
- lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_control_flow)],
+ lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW)],
lief.ARCHITECTURES.ARM: BASE_ELF,
lief.ARCHITECTURES.ARM64: BASE_ELF,
lief.ARCHITECTURES.PPC: BASE_ELF,
@@ -240,39 +240,24 @@ CHECKS = {
lief.EXE_FORMATS.MACHO: {
lief.ARCHITECTURES.X86: BASE_MACHO + [('PIE', check_PIE),
('NX', check_NX),
- ('CONTROL_FLOW', check_MACHO_control_flow)],
- lief.ARCHITECTURES.ARM64: BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_branch_protection)],
+ ('CONTROL_FLOW', check_MACHO_CONTROL_FLOW)],
+ lief.ARCHITECTURES.ARM64: BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_BRANCH_PROTECTION)],
}
}
if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
- try:
- binary = lief.parse(filename)
- etype = binary.format
- arch = binary.abstract.header.architecture
- binary.concrete
-
- if etype == lief.EXE_FORMATS.UNKNOWN:
- print(f'{filename}: unknown executable format')
- retval = 1
- continue
-
- if arch == lief.ARCHITECTURES.NONE:
- print(f'{filename}: unknown architecture')
- retval = 1
- continue
-
- failed: list[str] = []
- for (name, func) in CHECKS[etype][arch]:
- if not func(binary):
- failed.append(name)
- if failed:
- print(f'{filename}: failed {" ".join(failed)}')
- retval = 1
- except IOError:
- print(f'{filename}: cannot open')
+ binary = lief.parse(filename)
+ etype = binary.format
+ arch = binary.abstract.header.architecture
+ binary.concrete
+
+ failed: list[str] = []
+ for (name, func) in CHECKS[etype][arch]:
+ if not func(binary):
+ failed.append(name)
+ if failed:
+ print(f'{filename}: failed {" ".join(failed)}')
retval = 1
sys.exit(retval)
-
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index c4e6bc81e1..cff5a9b480 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -299,22 +299,14 @@ lief.EXE_FORMATS.PE: [
if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
- try:
- binary = lief.parse(filename)
- etype = binary.format
- if etype == lief.EXE_FORMATS.UNKNOWN:
- print(f'{filename}: unknown executable format')
- retval = 1
- continue
-
- failed: list[str] = []
- for (name, func) in CHECKS[etype]:
- if not func(binary):
- failed.append(name)
- if failed:
- print(f'{filename}: failed {" ".join(failed)}')
- retval = 1
- except IOError:
- print(f'{filename}: cannot open')
+ binary = lief.parse(filename)
+ etype = binary.format
+
+ failed: list[str] = []
+ for (name, func) in CHECKS[etype]:
+ if not func(binary):
+ failed.append(name)
+ if failed:
+ print(f'{filename}: failed {" ".join(failed)}')
retval = 1
sys.exit(retval)
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index de372cbd39..4bec6bfe7c 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -59,33 +59,20 @@ class TestSecurityChecks(unittest.TestCase):
arch = get_arch(cxx, source, executable)
if arch == lief.ARCHITECTURES.X86:
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-zexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE NX RELRO CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE RELRO CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE RELRO CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-pie','-fPIE', '-Wl,-z,separate-code']),
- (1, executable+': failed RELRO CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,noseparate-code']),
- (1, executable+': failed separate_code CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
- (1, executable+': failed CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full']),
- (0, ''))
+ pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full']
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
else:
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-zexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE NX RELRO'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE RELRO'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
- (1, executable+': failed PIE RELRO'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-znorelro','-pie','-fPIE', '-Wl,-z,separate-code']),
- (1, executable+': failed RELRO'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,noseparate-code']),
- (1, executable+': failed separate_code'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-znoexecstack','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
- (0, ''))
+ pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code']
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
clean_files(source, executable)
@@ -95,20 +82,16 @@ class TestSecurityChecks(unittest.TestCase):
cxx = determine_wellknown_cmd('CXX', 'x86_64-w64-mingw32-g++')
write_testcode(source)
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--disable-nxcompat','-Wl,--disable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-no-pie','-fno-PIE','-fno-stack-protector']),
- (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION CONTROL_FLOW Canary'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--disable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-no-pie','-fno-PIE','-fstack-protector-all', '-lssp']),
- (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-no-pie','-fno-PIE','-fstack-protector-all', '-lssp']),
- (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
- (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA CONTROL_FLOW')) # -pie -fPIE does nothing unless --dynamicbase is also supplied
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--disable-high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
- (1, executable+': failed HIGH_ENTROPY_VA CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
- (1, executable+': failed CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE', '-fcf-protection=full','-fstack-protector-all', '-lssp']),
- (0, ''))
+ pass_flags = ['-Wl,--nxcompat', '-Wl,--enable-reloc-section', '-Wl,--dynamicbase', '-Wl,--high-entropy-va', '-pie', '-fPIE', '-fcf-protection=full', '-fstack-protector-all', '-lssp']
+
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY'))
+ # https://github.com/lief-project/LIEF/issues/1076 - in future, we could test this individually.
+ # self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-reloc-section']), (1, executable + ': failed RELOC_SECTION'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-nxcompat']), (1, executable + ': failed NX'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-dynamicbase']), (1, executable + ': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA')) # -pie -fPIE does nothing without --dynamicbase
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-high-entropy-va']), (1, executable + ': failed HIGH_ENTROPY_VA'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
clean_files(source, executable)
@@ -120,27 +103,21 @@ class TestSecurityChecks(unittest.TestCase):
arch = get_arch(cxx, source, executable)
if arch == lief.ARCHITECTURES.X86:
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-no_fixup_chains']),
- (1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS PIE CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-fixup_chains']),
- (1, executable+': failed NOUNDEFS Canary CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains']),
- (1, executable+': failed NOUNDEFS CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-fstack-protector-all', '-Wl,-fixup_chains']),
- (1, executable+': failed CONTROL_FLOW'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']),
- (0, ''))
+ pass_flags = ['-Wl,-pie', '-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_pie', '-Wl,-no_fixup_chains']), (1, executable+': failed FIXUP_CHAINS PIE')) # -fixup_chains is incompatible with -no_pie
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_fixup_chains']), (1, executable + ': failed FIXUP_CHAINS'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-flat_namespace']), (1, executable + ': failed NOUNDEFS'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
else:
- # arm64 darwin doesn't support non-PIE binaries, control flow or executable stacks
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-no_fixup_chains']),
- (1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS BRANCH_PROTECTION'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
- (1, executable+': failed NOUNDEFS Canary'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
- (1, executable+': failed NOUNDEFS'))
- self.assertEqual(call_security_check(cxx, source, executable, ['-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
- (0, ''))
-
+ # arm64 darwin doesn't support non-PIE binaries or executable stacks
+ pass_flags = ['-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti']
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-mbranch-protection=none']), (1, executable + ': failed BRANCH_PROTECTION'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_fixup_chains']), (1, executable + ': failed FIXUP_CHAINS'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-flat_namespace']), (1, executable + ': failed NOUNDEFS'))
+ self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
clean_files(source, executable)