aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2023-02-03 16:16:56 +0000
committerfanquake <fanquake@gmail.com>2024-09-09 12:35:13 +0100
commitbe4f78275fa6608b11377dd5a29a809597d3fe8d (patch)
tree0bdd8bd75c4531a582f8e14d290be1c04ddda281 /contrib
parent94bc3c4cc0845b5238c11e1c66d70882e459742a (diff)
contrib: test for FORTIFY_SOURCE in security-check.py
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/devtools/security-check.py30
-rwxr-xr-xcontrib/devtools/test-security-check.py11
2 files changed, 33 insertions, 8 deletions
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 46f9ee915f..4c20685b51 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -7,6 +7,7 @@ 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.
'''
+import re
import sys
import lief
@@ -116,6 +117,25 @@ def check_ELF_CONTROL_FLOW(binary) -> bool:
return True
return False
+def check_ELF_FORTIFY(binary) -> bool:
+
+ # bitcoin-util does not currently contain any fortified functions
+ if 'Bitcoin Core bitcoin-util utility version ' in binary.strings:
+ return True
+
+ chk_funcs = set()
+
+ for sym in binary.imported_symbols:
+ match = re.search(r'__[a-z]*_chk', sym.name)
+ if match:
+ chk_funcs.add(match.group(0))
+
+ # ignore stack-protector and bdb
+ chk_funcs.discard('__stack_chk')
+ chk_funcs.discard('__db_chk')
+
+ return len(chk_funcs) >= 1
+
def check_PE_DYNAMIC_BASE(binary) -> bool:
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists
@@ -228,11 +248,11 @@ BASE_MACHO = [
CHECKS = {
lief.EXE_FORMATS.ELF: {
- 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,
- lief.ARCHITECTURES.RISCV: BASE_ELF,
+ lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW), ('FORTIFY', check_ELF_FORTIFY)],
+ lief.ARCHITECTURES.ARM: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
+ lief.ARCHITECTURES.ARM64: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
+ lief.ARCHITECTURES.PPC: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
+ lief.ARCHITECTURES.RISCV: BASE_ELF, # Skip FORTIFY. See https://github.com/lief-project/LIEF/issues/1082.
},
lief.EXE_FORMATS.PE: {
lief.ARCHITECTURES.X86: BASE_PE,
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 4bec6bfe7c..99f171608d 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -59,17 +59,22 @@ class TestSecurityChecks(unittest.TestCase):
arch = get_arch(cxx, source, executable)
if arch == lief.ARCHITECTURES.X86:
- pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full']
+ pass_flags = ['-D_FORTIFY_SOURCE=3', '-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 + ['-U_FORTIFY_SOURCE']), (1, executable + ': failed FORTIFY'))
self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, ''))
else:
- pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code']
+ pass_flags = ['-D_FORTIFY_SOURCE=3', '-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'))
+ # LIEF fails to parse RISC-V with no PIE correctly, and doesn't detect the fortified function,
+ # so skip this test for RISC-V (for now). See https://github.com/lief-project/LIEF/issues/1082.
+ if arch != lief.ARCHITECTURES.RISCV:
+ 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 + ['-U_FORTIFY_SOURCE']), (1, executable + ': failed FORTIFY'))
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, ''))