build: add support for checksec.sh based mitigation reports
Checksec is a bash script to check the properties of executables (like PIE, RELRO, Canaries, ASLR, Fortify Source). Add an optional build feature, which when enabled is going to create a JSON report about the state of mitigations in the produced binaries. Upstream-Status: Submitted [https://github.com/openwrt/openwrt/pull/14569] Signed-off-by: Cedric DOURLENT <cedric.dourlent@softathome.com>
This commit is contained in:
parent
4f09b50710
commit
8eaacac69a
@ -34,6 +34,13 @@ menu "Global build settings"
|
||||
directory containing Software Bill Of Materials in CycloneDX
|
||||
format.
|
||||
|
||||
config JSON_CHECKSEC_MITIGATIONS_REPORT
|
||||
bool
|
||||
prompt "Create binaries hardening report (based on checksec.sh)"
|
||||
help
|
||||
Create a JSON file *.checksec.json in the build
|
||||
directory containing the result of checksec.
|
||||
|
||||
config ALL_NONSHARED
|
||||
bool "Select all target specific packages by default"
|
||||
select ALL_KMODS
|
||||
|
||||
@ -320,6 +320,10 @@ ifneq ($(CONFIG_JSON_CYCLONEDX_SBOM),)
|
||||
$(if $(IB),$(TOPDIR)/.packageinfo, $(TMP_DIR)/.packageinfo) \
|
||||
$(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).manifest > \
|
||||
$(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).bom.cdx.json
|
||||
$(if $(CONFIG_JSON_CHECKSEC_MITIGATIONS_REPORT), \
|
||||
$(PYTHON) $(SCRIPT_DIR)/checksec-mitigation-report.py \
|
||||
$(BUILD_DIR) \
|
||||
$(BIN_DIR)/$(IMG_PREFIX)$(if $(PROFILE_SANITIZED),-$(PROFILE_SANITIZED)).checksec.json)
|
||||
endif
|
||||
endef
|
||||
|
||||
|
||||
@ -224,6 +224,19 @@ endif
|
||||
$(if $(PROVIDES),@for pkg in $(filter-out $(1),$(PROVIDES)); do cp $(PKG_INFO_DIR)/$(1).provides $(PKG_INFO_DIR)/$$$$pkg.provides; done)
|
||||
$(CheckDependencies)
|
||||
|
||||
# We need to run checksec.sh before stripping for working stack canary check.
|
||||
ifneq ($$(CONFIG_JSON_CHECKSEC_MITIGATIONS_REPORT),)
|
||||
( \
|
||||
mkdir -p $(PKG_BUILD_DIR)/CHECKSEC/$(1)/ && \
|
||||
checksec --output=json --dir=$$(IDIR_$(1)) > $(PKG_BUILD_DIR)/CHECKSEC/$(1)/checksec_report.json && \
|
||||
$(PYTHON) $(SCRIPT_DIR)/checksec-format-output.py \
|
||||
$(PKG_BUILD_DIR)/CHECKSEC/$(1)/checksec_report.json \
|
||||
$$(IDIR_$(1)) \
|
||||
$(PKG_NAME) \
|
||||
$(if $(PKG_VERSION),$(PKG_VERSION),"N/A") \
|
||||
$(PKG_BUILD_DIR)/CHECKSEC/$(1)/checksec_report_formatted.json \
|
||||
)
|
||||
endif
|
||||
$(RSTRIP) $$(IDIR_$(1))
|
||||
|
||||
ifneq ($$(CONFIG_IPK_FILES_CHECKSUMS),)
|
||||
|
||||
34
scripts/checksec-format-output.py
Executable file
34
scripts/checksec-format-output.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
from sys import argv
|
||||
import json
|
||||
|
||||
if len(argv) != 6:
|
||||
print("Usage: checkseck_format_output.py input build_dir package_name package_version output")
|
||||
exit(1)
|
||||
|
||||
input_path = Path(argv[1])
|
||||
builddir_path = argv[2]
|
||||
name = argv[3]
|
||||
version = argv[4]
|
||||
output_path = Path(argv[5])
|
||||
output={}
|
||||
|
||||
with open(input_path, "r") as read_file:
|
||||
output["elflist"]={}
|
||||
data = json.load(read_file)
|
||||
del data['dir']
|
||||
for k in list(data):
|
||||
new=k.replace(builddir_path, "")
|
||||
output["elflist"][new]=data.pop(k)
|
||||
output["elflist"][new]["rpath"]="no"
|
||||
output["elflist"][new]["filename"] = output["elflist"][new]["filename"].replace(builddir_path, "")
|
||||
|
||||
|
||||
|
||||
output['package'] = name
|
||||
output['version'] = version
|
||||
with open(output_path, "w") as write_file:
|
||||
json.dump(output, write_file)
|
||||
|
||||
109
scripts/checksec-mitigation-report.py
Executable file
109
scripts/checksec-mitigation-report.py
Executable file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
from sys import argv
|
||||
import json
|
||||
|
||||
if len(argv) != 3:
|
||||
print("Usage: checksec_mitigation_report.py build_dir output")
|
||||
exit(1)
|
||||
|
||||
builddir_path = argv[1]
|
||||
output_path = Path(argv[2])
|
||||
|
||||
print(builddir_path)
|
||||
print(output_path)
|
||||
|
||||
|
||||
output = {
|
||||
}
|
||||
|
||||
with open(output_path, "w") as write_file:
|
||||
output["packagelist"]=[]
|
||||
|
||||
builddir = Path(builddir_path)
|
||||
for report in list(builddir.rglob("checksec_report_formatted.json")):
|
||||
with open(report, "r") as read_file:
|
||||
data = json.load(read_file)
|
||||
output["packagelist"].append(data)
|
||||
json.dump(output, write_file)
|
||||
|
||||
print("#############################################")
|
||||
print("############# HARDENING RESULTS #############")
|
||||
print("#############################################")
|
||||
|
||||
# elf list
|
||||
number_of_elf=0
|
||||
for package in output["packagelist"]:
|
||||
number_of_elf += len(package["elflist"])
|
||||
|
||||
print("NB_ELF = " + str(number_of_elf))
|
||||
|
||||
#relro result
|
||||
number_of_relro=0
|
||||
relro_positive_result=["n/a", "full", "partial"]
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["relro"] in relro_positive_result:
|
||||
number_of_relro+=1
|
||||
|
||||
# now result
|
||||
number_of_now=0
|
||||
now_positive_result=["n/a", "full"]
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["relro"] in now_positive_result :
|
||||
number_of_now+=1;
|
||||
|
||||
# stack protector result
|
||||
number_of_ssp=0
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["canary"] == "yes":
|
||||
number_of_ssp+=1;
|
||||
|
||||
# nx result
|
||||
number_of_nx=0
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["nx"] == "yes":
|
||||
number_of_nx+=1;
|
||||
|
||||
# PIE result
|
||||
number_of_pie=0
|
||||
pie_positive_result=["yes", "dso"]
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["pie"] in pie_positive_result:
|
||||
number_of_pie+=1;
|
||||
|
||||
# rpath
|
||||
number_of_rpath=0
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["rpath"] == "yes":
|
||||
number_of_rpath+=1;
|
||||
|
||||
# fortify
|
||||
number_of_fortify=0
|
||||
for package in output["packagelist"]:
|
||||
for k in list(package["elflist"]):
|
||||
elf=package["elflist"][k]
|
||||
if elf["fortify_source"] != "no" :
|
||||
number_of_fortify+=1;
|
||||
|
||||
|
||||
print('RELRO = {:2.2%}'.format(number_of_relro / number_of_elf))
|
||||
print('NOW = {:2.2%}'.format(number_of_now / number_of_elf))
|
||||
print('NX = {:2.2%}'.format(number_of_nx / number_of_elf))
|
||||
print('PIE = {:2.2%}'.format(number_of_pie / number_of_elf))
|
||||
print('SSP = {:2.2%}'.format(number_of_ssp / number_of_elf))
|
||||
print('FORTIFY = {:2.2%}'.format(number_of_fortify / number_of_elf))
|
||||
print('RPATH = {:2.2%}'.format(number_of_rpath / number_of_elf))
|
||||
|
||||
@ -70,6 +70,7 @@ tools-y += util-linux
|
||||
tools-y += xz
|
||||
tools-y += zip
|
||||
tools-y += zlib
|
||||
tools-$(if $(CONFIG_JSON_CHECKSEC_MITIGATIONS_REPORT),y) += checksec.sh
|
||||
tools-$(if $(CONFIG_BUILD_ALL_HOST_TOOLS),y) += liblzo
|
||||
tools-$(if $(CONFIG_BUILD_ALL_HOST_TOOLS)$(BUILD_B43_TOOLS),y) += b43-tools
|
||||
tools-$(if $(CONFIG_BUILD_ALL_HOST_TOOLS)$(BUILD_BZIP2_TOOLS),y) += bzip2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user