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:
Cedric DOURLENT 2024-02-05 13:52:50 +01:00 committed by Petr Štetiar
parent 4f09b50710
commit 8eaacac69a
No known key found for this signature in database
GPG Key ID: 58EE120F30CC02D3
6 changed files with 168 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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),)

View 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)

View 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))

View File

@ -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