Skip to content

Commit

Permalink
Merge pull request #565 from fkie-cad/SD1409-checksec-sh-2
Browse files Browse the repository at this point in the history
checksec.sh eingebunden
  • Loading branch information
jstucke committed Apr 21, 2021
2 parents dbd9390 + 86a4828 commit c90e7e5
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 176 deletions.
218 changes: 143 additions & 75 deletions src/plugins/analysis/checksec/code/checksec.py
Original file line number Diff line number Diff line change
@@ -1,113 +1,181 @@
import json
import re
from pathlib import Path

from common_helper_process import execute_shell_command_get_return_code

from common_helper_process import execute_shell_command
from analysis.PluginBase import AnalysisBasePlugin

READELF_CALL = 'readelf -W -l -d --dyn-syms -h {} '
SHELL_SCRIPT = Path(__file__).parent.parent / 'bin' / 'checksec'


class AnalysisPlugin(AnalysisBasePlugin):
NAME = 'exploit_mitigations'
DESCRIPTION = 'analyses ELF binaries within a firmware for present exploit mitigation techniques'
DEPENDENCIES = ['file_type']
MIME_WHITELIST = ['application/x-executable', 'application/x-object', 'application/x-sharedlib']
VERSION = '0.1.3'
VERSION = '0.1.4'

def __init__(self, plugin_administrator, config=None, recursive=True):
self.config = config

if not SHELL_SCRIPT.exists():
raise RuntimeError(f'{SHELL_SCRIPT} not present. Re-run the plugin or backend installation.')

super().__init__(plugin_administrator, config=config, recursive=recursive, plugin_path=__file__)

def process_object(self, file_object):
try:
if re.search(r'.*elf.*', file_object.processed_analysis['file_type']['full'].lower()) is not None:

mitigation_dict, mitigation_dict_summary = check_mitigations(file_object.file_path)
file_object.processed_analysis[self.NAME] = mitigation_dict
file_object.processed_analysis[self.NAME]['summary'] = list(mitigation_dict_summary.keys())
else:
file_object.processed_analysis[self.NAME]['summary'] = []
except Exception as e:
except (IndexError, json.JSONDecodeError, ValueError) as error:
file_object.processed_analysis[self.NAME]['summary'] = [
'Error - Firmware could not be processed properly: {}'.format(e)
'Error - Firmware could not be processed properly: {}'.format(error)
]
return file_object


def get_readelf_result(path):
readelf_full = execute_shell_command(READELF_CALL.format(path))
return readelf_full
def execute_checksec_script(file_path):
checksec_result, return_code = execute_shell_command_get_return_code(f'{SHELL_SCRIPT} --file={file_path} --format=json --extended')
if return_code != 0:
raise ValueError(f'Checksec script exited with non-zero return code {return_code}')
return json.loads(checksec_result)[str(file_path)]


def check_relro(file_path, dict_res, dict_sum, readelf):
if re.search(r'GNU_RELRO', readelf):
if re.search(r'BIND_NOW', readelf):
dict_sum.update({'RELRO fully enabled': file_path})
dict_res.update({'RELRO': 'fully enabled'})
else:
dict_sum.update({'RELRO partially enabled': file_path})
dict_res.update({'RELRO': 'partially enabled'})
else:
dict_sum.update({'RELRO disabled': file_path})
dict_res.update({'RELRO': 'disabled'})
def check_mitigations(file_path):
mitigations, summary = {}, {}
checksec_result = execute_checksec_script(file_path)

check_relro(file_path, mitigations, summary, checksec_result)
check_nx(file_path, mitigations, summary, checksec_result)
check_canary(file_path, mitigations, summary, checksec_result)
check_pie(file_path, mitigations, summary, checksec_result)
check_fortify_source(file_path, mitigations, summary, checksec_result)
check_clang_cfi(file_path, mitigations, summary, checksec_result)
check_clang_safestack(file_path, mitigations, summary, checksec_result)
check_stripped_symbols(file_path, mitigations, summary, checksec_result)
check_runpath(file_path, mitigations, summary, checksec_result)
check_rpath(file_path, mitigations, summary, checksec_result)

return mitigations, summary


def check_relro(file_path, mitigations, summary, checksec_result):
if checksec_result['relro'] == 'full':
summary.update({'RELRO fully enabled': file_path})
mitigations.update({'RELRO': 'fully enabled'})

elif checksec_result['relro'] == 'partial':
summary.update({'RELRO partially enabled': file_path})
mitigations.update({'RELRO': 'partially enabled'})

elif checksec_result['relro'] == 'no':
summary.update({'RELRO disabled': file_path})
mitigations.update({'RELRO': 'disabled'})


def check_fortify_source(file_path, mitigations, summary, checksec_result):
if checksec_result['fortify_source'] == 'yes':
summary.update({'FORTIFY_SOURCE enabled': file_path})
mitigations.update({'FORTIFY_SOURCE': 'enabled'})

elif checksec_result['fortify_source'] == 'no':
summary.update({'FORTIFY_SOURCE disabled': file_path})
mitigations.update({'FORTIFY_SOURCE': 'disabled'})


def check_pie(file_path, mitigations, summary, checksec_result):
if checksec_result['pie'] == 'yes':
summary.update({'PIE enabled': file_path})
mitigations.update({'PIE': 'enabled'})

elif checksec_result['pie'] == 'no':
summary.update({'PIE disabled': file_path})
mitigations.update({'PIE': 'disabled'})

elif checksec_result['pie'] == 'dso':
summary.update({'PIE/DSO present': file_path})
mitigations.update({'PIE': 'DSO'})

elif checksec_result['pie'] == 'rel':
summary.update({'PIE/REL present': file_path})
mitigations.update({'PIE': 'REL'})

def check_fortify(file_path, dict_res, dict_sum, readelf):
if re.search(r'__\w+_chk@', readelf):
dict_sum.update({'FORTIFY_SOURCE enabled': file_path})
dict_res.update({'FORTIFY_SOURCE': 'enabled'})
else:
dict_sum.update({'FORTIFY_SOURCE disabled': file_path})
dict_res.update({'FORTIFY_SOURCE': 'disabled'})


def check_pie(file_path, dict_res, dict_sum, readelf):
if re.search(r'Type:\s*EXEC', readelf):
dict_sum.update({'PIE disabled': file_path})
dict_res.update({'PIE': 'disabled'})
elif re.search(r'Type:\s*DYN', readelf):
if re.search(r'\(DEBUG\)', readelf):
dict_sum.update({'PIE enabled': file_path})
dict_res.update({'PIE': 'enabled'})
else:
dict_sum.update({'PIE/DSO present': file_path})
dict_res.update({'PIE': 'DSO'})
else:
dict_sum.update({'PIE - invalid ELF file': file_path})
dict_res.update({'PIE': 'invalid ELF file'})


def check_nx_or_canary(file_path, dict_res, dict_sum, readelf, flag):
if flag == 'NX':
nx_off = re.search(r'GNU_STACK[\s0-9a-z]*RWE', readelf)
if nx_off is None:
mitigation_off = False
else:
mitigation_off = True
elif flag == 'Canary':
canary_on = re.search(r'__stack_chk_fail', readelf)
if canary_on is None:
mitigation_off = True
else:
mitigation_off = False
else:
return None
_set_nx_canary_result(dict_res, dict_sum, file_path, flag, mitigation_off)
summary.update({'PIE - invalid ELF file': file_path})
mitigations.update({'PIE': 'invalid ELF file'})


def _set_nx_canary_result(dict_res, dict_sum, file_path, flag, mitigation_off):
if mitigation_off is True:
dict_sum.update({'{} disabled'.format(flag): file_path})
dict_res.update({flag: 'disabled'})
elif mitigation_off is False:
dict_sum.update({'{} enabled'.format(flag): file_path})
dict_res.update({flag: 'enabled'})
def check_nx(file_path, mitigations, summary, checksec_result):
if checksec_result['nx'] == 'yes':
summary.update({'NX enabled': file_path})
mitigations.update({'NX': 'enabled'})

elif checksec_result['nx'] == 'no':
summary.update({'NX disabled': file_path})
mitigations.update({'NX': 'disabled'})

def check_mitigations(file_path):
dict_res, dict_sum = {}, {}
readelf_results = get_readelf_result(file_path)
check_relro(file_path, dict_res, dict_sum, readelf_results)
check_nx_or_canary(file_path, dict_res, dict_sum, readelf_results, 'NX')
check_nx_or_canary(file_path, dict_res, dict_sum, readelf_results, 'Canary')
check_pie(file_path, dict_res, dict_sum, readelf_results)
check_fortify(file_path, dict_res, dict_sum, readelf_results)
return dict_res, dict_sum

def check_canary(file_path, mitigations, summary, checksec_result):
if checksec_result['canary'] == 'yes':
summary.update({'CANARY enabled': file_path})
mitigations.update({'CANARY': 'enabled'})

elif checksec_result['canary'] == 'no':
summary.update({'CANARY disabled': file_path})
mitigations.update({'CANARY': 'disabled'})


def check_clang_cfi(file_path, mitigations, summary, checksec_result):
if checksec_result['clangcfi'] == 'yes':
summary.update({'CLANGCFI enabled': file_path})
mitigations.update({'CLANGCFI': 'enabled'})

elif checksec_result['clangcfi'] == 'no':
summary.update({'CLANGCFI disabled': file_path})
mitigations.update({'CLANGCFI': 'disabled'})


def check_clang_safestack(file_path, mitigations, summary, checksec_result):
if checksec_result['safestack'] == 'yes':
summary.update({'SAFESTACK enabled': file_path})
mitigations.update({'SAFESTACK': 'enabled'})

elif checksec_result['safestack'] == 'no':
summary.update({'SAFESTACK disabled': file_path})
mitigations.update({'SAFESTACK': 'disabled'})


def check_rpath(file_path, mitigations, summary, checksec_result):
if checksec_result['rpath'] == 'yes':
summary.update({'RPATH enabled': file_path})
mitigations.update({'RPATH': 'enabled'})

elif checksec_result['rpath'] == 'no':
summary.update({'RPATH disabled': file_path})
mitigations.update({'RPATH': 'disabled'})


def check_runpath(file_path, mitigations, summary, checksec_result):
if checksec_result['runpath'] == 'yes':
summary.update({'RUNPATH enabled': file_path})
mitigations.update({'RUNPATH': 'enabled'})

elif checksec_result['runpath'] == 'no':
summary.update({'RUNPATH disabled': file_path})
mitigations.update({'RUNPATH': 'disabled'})


def check_stripped_symbols(file_path, mitigations, summary, checksec_result):
if checksec_result['symbols'] == 'yes':
summary.update({'STRIPPED SYMBOLS disabled': file_path})
mitigations.update({'STRIPPED SYMBOLS': 'disabled'})

elif checksec_result['symbols'] == 'no':
summary.update({'STRIPPED SYMBOLS enabled': file_path})
mitigations.update({'STRIPPED SYMBOLS': 'enabled'})
13 changes: 13 additions & 0 deletions src/plugins/analysis/checksec/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

# change cwd to current file's directory
cd "$( dirname "${BASH_SOURCE[0]}" )" || exit 1

echo "------------------------------------"
echo " Install checksec.sh "
echo "------------------------------------"

rm -rf bin
git clone https://github.com/slimm609/checksec.sh.git bin || exit 1

exit 0
Binary file added src/plugins/analysis/checksec/test/data/Hallo.o
Binary file not shown.
Binary file added src/plugins/analysis/checksec/test/data/Hallo.out
Binary file not shown.
Binary file added src/plugins/analysis/checksec/test/data/Hallo.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit c90e7e5

Please sign in to comment.