2018-04-13 17:37:53 +02:00
|
|
|
#!/usr/bin/env python3
|
2022-12-25 00:49:50 +01:00
|
|
|
# Copyright (c) 2015-2022 The Bitcoin Core developers
|
2016-09-11 23:32:22 +02:00
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2015-10-19 14:53:56 +02:00
|
|
|
'''
|
|
|
|
Test script for security-check.py
|
|
|
|
'''
|
2023-04-21 11:28:53 +02:00
|
|
|
import lief
|
2021-03-13 17:27:11 +01:00
|
|
|
import os
|
2015-10-19 14:53:56 +02:00
|
|
|
import subprocess
|
|
|
|
import unittest
|
|
|
|
|
2021-01-21 19:52:40 +01:00
|
|
|
from utils import determine_wellknown_cmd
|
|
|
|
|
2015-10-19 14:53:56 +02:00
|
|
|
def write_testcode(filename):
|
2018-06-12 17:49:20 +02:00
|
|
|
with open(filename, 'w', encoding="utf8") as f:
|
2015-10-19 14:53:56 +02:00
|
|
|
f.write('''
|
|
|
|
#include <stdio.h>
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
printf("the quick brown fox jumps over the lazy god\\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
''')
|
|
|
|
|
2021-03-13 17:27:11 +01:00
|
|
|
def clean_files(source, executable):
|
|
|
|
os.remove(source)
|
|
|
|
os.remove(executable)
|
|
|
|
|
2023-06-29 23:41:27 +02:00
|
|
|
def call_security_check(cc: str, source: str, executable: str, options) -> tuple:
|
2021-10-01 02:17:53 +02:00
|
|
|
# This should behave the same as AC_TRY_LINK, so arrange well-known flags
|
|
|
|
# in the same order as autoconf would.
|
|
|
|
#
|
|
|
|
# See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for
|
|
|
|
# reference.
|
2023-10-25 00:55:17 +02:00
|
|
|
env_flags: list[str] = []
|
2021-10-01 02:17:53 +02:00
|
|
|
for var in ['CFLAGS', 'CPPFLAGS', 'LDFLAGS']:
|
|
|
|
env_flags += filter(None, os.environ.get(var, '').split(' '))
|
|
|
|
|
|
|
|
subprocess.run([*cc,source,'-o',executable] + env_flags + options, check=True)
|
2023-01-17 21:46:35 +01:00
|
|
|
p = subprocess.run([os.path.join(os.path.dirname(__file__), 'security-check.py'), executable], stdout=subprocess.PIPE, text=True)
|
2020-05-15 04:52:20 +02:00
|
|
|
return (p.returncode, p.stdout.rstrip())
|
2015-10-19 14:53:56 +02:00
|
|
|
|
2021-10-15 07:41:49 +02:00
|
|
|
def get_arch(cc, source, executable):
|
|
|
|
subprocess.run([*cc, source, '-o', executable], check=True)
|
|
|
|
binary = lief.parse(executable)
|
|
|
|
arch = binary.abstract.header.architecture
|
|
|
|
os.remove(executable)
|
|
|
|
return arch
|
|
|
|
|
2015-10-19 14:53:56 +02:00
|
|
|
class TestSecurityChecks(unittest.TestCase):
|
|
|
|
def test_ELF(self):
|
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1'
|
2021-01-21 19:52:40 +01:00
|
|
|
cc = determine_wellknown_cmd('CC', 'gcc')
|
2015-10-19 14:53:56 +02:00
|
|
|
write_testcode(source)
|
2021-10-15 07:41:49 +02:00
|
|
|
arch = get_arch(cc, source, executable)
|
2015-10-19 14:53:56 +02:00
|
|
|
|
2021-10-15 07:41:49 +02:00
|
|
|
if arch == lief.ARCHITECTURES.X86:
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE NX RELRO Canary CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE RELRO Canary CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE RELRO CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed RELRO CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,noseparate-code']),
|
|
|
|
(1, executable+': failed separate_code CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full']),
|
|
|
|
(0, ''))
|
|
|
|
else:
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE NX RELRO Canary'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE RELRO Canary'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed PIE RELRO'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE', '-Wl,-z,separate-code']),
|
|
|
|
(1, executable+': failed RELRO'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,noseparate-code']),
|
|
|
|
(1, executable+': failed separate_code'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
|
|
|
|
(0, ''))
|
2015-10-19 14:53:56 +02:00
|
|
|
|
2021-03-13 17:27:11 +01:00
|
|
|
clean_files(source, executable)
|
|
|
|
|
2020-03-26 04:37:32 +01:00
|
|
|
def test_PE(self):
|
2018-04-26 18:43:01 +02:00
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1.exe'
|
2021-01-21 19:52:40 +01:00
|
|
|
cc = determine_wellknown_cmd('CC', 'x86_64-w64-mingw32-gcc')
|
2018-04-26 18:43:01 +02:00
|
|
|
write_testcode(source)
|
|
|
|
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, 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(cc, source, executable, ['-Wl,--nxcompat','-Wl,--disable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-no-pie','-fno-PIE','-fstack-protector-all', '-lssp']),
|
2021-05-17 05:58:45 +02:00
|
|
|
(1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION CONTROL_FLOW'))
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-no-pie','-fno-PIE','-fstack-protector-all', '-lssp']),
|
2021-05-17 05:58:45 +02:00
|
|
|
(1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA CONTROL_FLOW'))
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--disable-dynamicbase','-Wl,--disable-high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
|
2021-05-17 05:58:45 +02:00
|
|
|
(1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA CONTROL_FLOW')) # -pie -fPIE does nothing unless --dynamicbase is also supplied
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--disable-high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
|
2021-05-17 05:58:45 +02:00
|
|
|
(1, executable+': failed HIGH_ENTROPY_VA CONTROL_FLOW'))
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE','-fstack-protector-all', '-lssp']),
|
2021-05-17 05:58:45 +02:00
|
|
|
(1, executable+': failed CONTROL_FLOW'))
|
2023-01-06 11:21:17 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE', '-fcf-protection=full','-fstack-protector-all', '-lssp']),
|
2020-03-26 04:37:32 +01:00
|
|
|
(0, ''))
|
2015-10-19 14:53:56 +02:00
|
|
|
|
2021-03-13 17:27:11 +01:00
|
|
|
clean_files(source, executable)
|
|
|
|
|
2020-03-24 05:37:57 +01:00
|
|
|
def test_MACHO(self):
|
|
|
|
source = 'test1.c'
|
|
|
|
executable = 'test1'
|
2021-01-21 19:52:40 +01:00
|
|
|
cc = determine_wellknown_cmd('CC', 'clang')
|
2020-03-24 05:37:57 +01:00
|
|
|
write_testcode(source)
|
2022-01-11 14:18:24 +01:00
|
|
|
arch = get_arch(cc, source, executable)
|
|
|
|
|
|
|
|
if arch == lief.ARCHITECTURES.X86:
|
2023-06-29 12:09:18 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector', '-Wl,-no_fixup_chains']),
|
|
|
|
(1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS PIE NX CONTROL_FLOW'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector', '-Wl,-fixup_chains']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed NOUNDEFS Canary PIE NX CONTROL_FLOW'))
|
2023-06-29 12:09:18 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fstack-protector-all', '-Wl,-fixup_chains']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed NOUNDEFS PIE NX CONTROL_FLOW'))
|
2023-06-29 12:09:18 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed NOUNDEFS PIE CONTROL_FLOW'))
|
2023-06-29 12:09:18 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all', '-Wl,-fixup_chains']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed PIE CONTROL_FLOW'))
|
2023-10-31 16:08:47 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all', '-Wl,-fixup_chains']),
|
2022-01-11 14:18:24 +01:00
|
|
|
(1, executable+': failed PIE CONTROL_FLOW'))
|
2023-10-31 16:08:47 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']),
|
2022-01-11 14:18:24 +01:00
|
|
|
(1, executable+': failed PIE'))
|
2023-10-31 16:08:47 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains']),
|
2022-01-11 14:18:24 +01:00
|
|
|
(0, ''))
|
|
|
|
else:
|
|
|
|
# arm64 darwin doesn't support non-PIE binaries, control flow or executable stacks
|
2023-06-29 12:09:18 +02:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-no_fixup_chains']),
|
2024-01-03 18:16:16 +01:00
|
|
|
(1, executable+': failed NOUNDEFS Canary FIXUP_CHAINS BRANCH_PROTECTION'))
|
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fno-stack-protector', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed NOUNDEFS Canary'))
|
2024-01-03 18:16:16 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-flat_namespace','-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
|
2023-05-31 15:32:10 +02:00
|
|
|
(1, executable+': failed NOUNDEFS'))
|
2024-01-03 18:16:16 +01:00
|
|
|
self.assertEqual(call_security_check(cc, source, executable, ['-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti']),
|
2022-01-11 14:18:24 +01:00
|
|
|
(0, ''))
|
2020-03-24 05:37:57 +01:00
|
|
|
|
|
|
|
|
2021-03-13 17:27:11 +01:00
|
|
|
clean_files(source, executable)
|
|
|
|
|
2015-10-19 14:53:56 +02:00
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|