Checksec 检查报告验收模板:区分加固缺失、检查失败与例外放行

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
354
文章
0
粉丝
Web安全1 37阅读模式
AI智能摘要
很多团队把“跑出了 checksec 报告”误当成“加固检查通过”,却不知红色结果既可能是加固缺失,也可能是检查失败,甚至只是工具版本不匹配。文章提供了一套可复用的验收资源包:最小测试程序、报告留存脚本、异常分类表和例外审批模板,帮你把三种状态彻底拆开。当报告出现黄色或红色时,你能否准确判断问题出在编译参数还是环境本身?
— AI 生成的文章内容摘要

在二进制交付流程里,checksec 几乎是加固检查的默认起点,但很多团队把“跑出了报告”和“检查通过”混为一谈。实际落地时,真正的问题往往不是工具不会用,而是报告结果无法被可靠解读:加固缺失、检查失败、例外放行三种状态混在一起,验收结论自然站不住脚。这篇文章给出的不是单条命令,而是一套可复用的验收资源包——包含最小测试程序、报告留存脚本、异常分类表和例外审批模板,让每次检查都能排障、能复测、能追踪例外。

先厘清三个容易混淆的状态

在动手搭资源包之前,有必要先把结果语义定清楚。checksec 的每个检查项会输出一个值(比如 Full RELRO)和一个驱动颜色的状态(green、yellow、red 或 unset),机器可读格式里则是对应的字符串。参考官方文档对颜色的定义,可以建立如下分类基准:

Checksec 检查报告验收模板:区分加固缺失、检查失败与例外放行
状态含义验收动作
green缓解措施存在且有效正常通过
yellow部分保护,或 checksec 无法确定状态(Unknown)需人工复核,不能直接算通过
red缓解缺失/禁用,或读取二进制时出错视为加固缺失或检查失败
italic / N/A该检查不适用于此类二进制记录为不适用,不参与通过判定

这里的关键认知是:red 既可能是加固缺失,也可能是检查失败。比如二进制文件权限不足、ELF 格式异常、工具版本不匹配,都可能让 checksec 报错并输出红色。如果不加区分地当作“加固缺失”来处理,排障方向就错了。因此,验收资源包的第一件事,就是把“未知结果”和“失败结果”从“确定缺失”里拆出来。

搭建最小可复现的测试环境

建议准备一个固定版本的测试二进制集合,而不是每次拿生产文件临时检查。这样做的目的是:当 checksec 输出异常时,你能用已知良好样本快速判断是工具问题还是目标文件问题。

最小测试程序可以包含三个文件:

  • hardened.c:启用常见加固选项编译的示例程序,作为“预期全绿”的基准。
  • plain.c:不加任何加固选项编译的示例程序,作为“预期多红”的对照。
  • Makefile:固化编译参数,确保每次重建结果一致。

编译时建议显式指定参数,而不是依赖编译器默认值。例如:

HARDEN_FLAGS = -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -pie -fPIE
PLAIN_FLAGS  = -no-pie -fno-stack-protector -Wl,-z,norelro

hardened: hardened.c
	gcc $(HARDEN_FLAGS) -o $@ $<

plain: plain.c
	gcc $(PLAIN_FLAGS) -o $@ $<

注意,-D_FORTIFY_SOURCE 需要在优化级别(-O1 及以上)下才生效,-Wl,-z,relro,-z,now 对应 Full RELRO。这些细节正是验收时容易踩坑的地方——不是 checksec 报错,而是编译参数本身没生效。

报告留存脚本:把输出变成可追溯证据

仅仅在终端看一眼彩色输出是不够的。验收资源包必须包含一个能自动留存机器可读报告的脚本,建议输出 JSON 或 YAML 格式,并附带工具版本、目标文件哈希和检查时间。

下面是一个最小脚本示例,使用 checksec 的 --output=json 参数(具体参数名以你所用版本的官方文档为准):

#!/usr/bin/env bash
set -euo pipefail

TARGET="$1"
OUT_DIR="./reports/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$OUT_DIR"

# 记录工具版本与目标文件信息
checksec --version > "$OUT_DIR/version.txt" 2>&1 || true
sha256sum "$TARGET" > "$OUT_DIR/hash.txt"

# 输出 JSON 报告
checksec --output=json --no-banner --no-headers file "$TARGET" 
  > "$OUT_DIR/report.json" 2> "$OUT_DIR/stderr.log"

# 记录退出码
echo "exit_code=$?" > "$OUT_DIR/exit_code.txt"

脚本里特意把标准错误单独留存,因为 checksec 的警告信息(比如无法读取某个段)往往只出现在 stderr,而这些信息恰恰是区分“加固缺失”和“检查失败”的重要线索。退出码同样关键——退出码为 0 只代表命令执行完成,不代表所有检查项都是 green。CI 里如果只判断退出码,很容易漏掉黄色或红色的检查项。

异常分类表:把“未知”变成可处理项

有了报告留存,下一步是建立异常分类表。这张表的目的是让任何人在看到一份报告时,都能按统一规则归类,而不是凭感觉判断。建议按以下维度分类:

分类判定依据处理动作
加固缺失状态为 red,且 stderr 无错误信息,文件哈希与构建产物一致回退到构建配置,调整编译参数后重建
检查失败状态为 red 或 yellow,但 stderr 有读取错误、权限警告,或工具版本与预期不符先排查环境问题,修复后重新检查
未知结果状态为 yellow 且无明确错误信息,或 checksec 输出 Unknown使用 readelf、objdump 等工具人工核验底层属性
不适用状态为 italic / N/A记录原因,不参与通过率统计
例外放行检查结果为缺失,但业务方确认可接受风险走例外审批流程,留存审批记录

一个常见的坑是:yellow 状态下的 Partial RELRO 到底算不算通过?严格来说,它表示部分保护存在,但 checksec 无法确认是否达到完整效果。对于安全要求较高的交付,建议将 Partial RELRO 视为待复核项,而不是直接放行。

例外审批模板:让放行有据可查

例外放行是最容易被滥用的环节。如果没有审批模板,口头一句“这个风险我们接受”就足以让一次检查形同虚设。建议例外审批单至少包含以下字段:

  • 目标文件路径与版本哈希
  • 涉及的具体检查项及当前值
  • 缺失加固的潜在影响(结合 OWASP C-Based Toolchain Hardening 等参考)
  • 业务方接受风险的理由
  • 审批人、审批日期与有效期
  • 后续缓解措施(如运行时防护、网络隔离)

一个可复用的模板示例:

# 例外审批单

- 文件:/opt/app/bin/service
- SHA256:`...`
- 检查项:RELRO
- 当前值:Partial RELRO
- 潜在影响:GOT 覆盖风险,可能导致控制流劫持
- 接受理由:该服务仅监听内网端口,且部署了 SELinux 限制
- 缓解措施:计划于 Q3 升级构建工具链后修复
- 有效期:2025-12-31
- 审批人:安全负责人 / 业务负责人

例外审批单应该与报告一起归档,而不是单独存放。这样在后续审计时,可以快速追溯到“为什么这个文件没有 Full RELRO”。

复核与回滚:调整构建配置后的闭环

当检查发现加固缺失时,正确的做法是调整构建配置、重建同一目标,然后重新跑 checksec 对比报告。这个流程需要固化,否则容易出现“改了配置但没生效”的情况。

建议在验收资源包里加入一个对比脚本,自动 diff 两次报告的 JSON 输出:

diff <(jq -S . reports/old/report.json) <(jq -S . reports/new/report.json)

如果 diff 结果为空,说明重建没有改变加固状态,需要回头检查编译参数是否真的传入了。如果 diff 显示部分字段从 red 变为 green,则说明修复生效。

同时,构建配置的变更必须可回滚。建议使用版本控制管理 Makefile 或构建脚本,每次调整后打 tag,方便快速回到上一个可用配置。如果使用 CI 流水线,这一步可以自动完成——构建失败或检查不通过时,自动回退到上一个通过的构建产物。

兼容性、脱敏与最小权限

最后补充三个容易被忽略的实践细节。

兼容性测试:checksec 的不同实现(Go 版、Python 版、Bash 版)对同一二进制的输出可能存在细微差异。验收资源包应固定使用同一版本的工具,并在报告中记录版本号。如果团队内有人使用不同实现,建议统一到官方推荐的版本。

报告脱敏:JSON 报告中可能包含文件路径、构建信息等敏感数据。如果报告需要分享给外部或存入公共仓库,建议在留存前做脱敏处理,例如替换绝对路径为相对路径,或删除无关的元数据字段。

最小权限:checksec 检查单个文件通常不需要 root 权限,但检查进程(proc 命令)可能需要读取 /proc 下的某些信息。在 CI 中运行检查时,应使用最小权限账号,避免以 root 身份执行不必要的扫描。

把资源包嵌入交付流程

这套资源包的最终形态,应该是一个包含以下内容的目录结构:

checksec-acceptance/
├── samples/           # 最小测试程序
│   ├── hardened.c
│   ├── plain.c
│   └── Makefile
├── scripts/
│   ├── run_check.sh   # 报告留存脚本
│   └── diff_reports.sh
├── templates/
│   ├── exception.md   # 例外审批模板
│   └── classification.md  # 异常分类表
└── README.md          # 使用说明

使用时,开发者只需将目标文件放入指定目录,运行 run_check.sh,即可得到一份带版本、哈希、退出码和 stderr 的完整报告。安全工程师在审核时,依据异常分类表快速归类,必要时生成例外审批单。整个流程从“看一眼输出”变成“留一套证据”,每一次检查结果都可追溯、可复测、可审计。

需要再次强调的是,checksec 的检查结果只代表编译时启用的缓解措施,不等同于漏洞扫描结论,更不能证明程序绝对安全。它回答的是“这个二进制有没有启用这些防护”,而不是“这个程序有没有漏洞”。把这一点写进资源包的 README,能避免团队对工具产生错误预期。

 
枫少@KillBoy
    • 山茱萸
      山茱萸 2

      这个分类表挺实用,以后验收报告不用凭感觉判断了

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证