From df4a84da32cdd21619c78a55bc045a83b9730caf Mon Sep 17 00:00:00 2001 From: zhangzhang Date: Fri, 12 Jun 2026 15:18:09 +0800 Subject: [PATCH 1/2] checksec --- .../checksec_binary/checksec_binary.sh | 281 ++++++++++++++++++ .../checksec_binary/checksec_binary.yaml | 21 ++ .../checksec_kernel/checksec_kernel.sh | 155 ++++++++++ .../checksec_kernel/checksec_kernel.yaml | 21 ++ 4 files changed, 478 insertions(+) create mode 100644 lava-testcases/security-test/checksec_binary/checksec_binary.sh create mode 100644 lava-testcases/security-test/checksec_binary/checksec_binary.yaml create mode 100644 lava-testcases/security-test/checksec_kernel/checksec_kernel.sh create mode 100644 lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml diff --git a/lava-testcases/security-test/checksec_binary/checksec_binary.sh b/lava-testcases/security-test/checksec_binary/checksec_binary.sh new file mode 100644 index 0000000..ddd0be1 --- /dev/null +++ b/lava-testcases/security-test/checksec_binary/checksec_binary.sh @@ -0,0 +1,281 @@ +#!/bin/bash + +set -x + +OUTPUT="$(pwd)/output" +mkdir -p "$OUTPUT" +RESULT_FILE="${OUTPUT}/result.txt" +BRES="bres.json" # 存放目录/bin的二进制加固结果 +UBRES="ubres.json" # 存放目录/usr/bin的二进制加固结果 +SBRES="sbres.json" # 存放目录/sbin的二进制加固结果 +USBRES="usbres.json" # 存放目录/usr/sbin的二进制加固结果 + +# 安装checksec +pkginstall(){ + dnf install -y git go + git clone https://github.com/slimm609/checksec.sh.git && cd checksec.sh + go build -o checksec main.go + chmod +x /usr/local/bin/checksec +} + +# 二进制加固验证 +binary_hardening_test(){ + # 检查 /bin /sbin /usr/bin /usr/sbin + checksec dir /bin -o json >> $BRES + checksec dir /usr/bin -o json >> $UBRES + checksec dir /sbin -o json >> $SBRES + checksec dir /usr/sbin -o json >> $USBRES +} + +# 处理验证结果为lava格式 +binary_risk_lava(){ + JSON_FILE="${1}" + DIRNAME="${2}" + OUTPUT_FILE="${3:-$RESULT_FILE}" + + [ ! -f "$JSON_FILE" ] && { echo "Usage: $0 [output_file]"; exit 1; } + + python3 - "$JSON_FILE" "$DIRNAME" "$OUTPUT_FILE" << 'PYEOF' +import json +import sys +import re + +with open(sys.argv[1]) as f: + data = json.load(f) +dirname = sys.argv[2] +out = open(sys.argv[3], 'a') + +# 0=INFO(通过), 1=LOW, 2=MEDIUM, 3=HIGH +HIGH_RISK = [ + # 特权程序 + r'su(d|do)$', r'passw(d|ord)$', r'login$', r'mount$', r'umount$', + r'ping$', r'ping6$', r'pkexec$', r'chsh$', r'chfn$', r'newgrp$', + r'gpasswd$', r'crontab$', r'at$', r'batch$', r'fusermount$', + r'ntfs-3g$', r'mount\.nfs$', r'mount\.cifs$', + # 系统服务/守护进程 + r'ssh(d|)$', r'Xorg$', r'Xwayland$', r'dbus-daemon$', r'dbus-broker$', + r'polkitd$', r'rtkit-daemon$', r'accounts-daemon$', r'colord$', + # systemd 核心组件 + r'systemd$', r'systemd-journald$', r'systemd-networkd$', r'systemd-resolved$', + r'systemd-logind$', r'systemd-machined$', r'systemd-timesyncd$', + # 网络服务 + r'NetworkManager$', r'firewalld$', r'auditd$', r'sssd$', r'httpd$', + r'nginx$', r'named$', r'bind$', r'mysqld$', r'mariadbd$', r'postgres$', + r'redis-server$', r'mongod$', r'memcached$', r'vsftpd$', r'proftpd$', + r'smbd$', r'nmbd$', r'rpc\..*', r'telnetd$', r'inetd$', r'xinetd$', + # 容器/虚拟化 + r'dockerd$', r'containerd$', r'containerd-shim$', r'crio$', r'podman$', + r'kubelet$', r'kube-proxy$', r'kube-apiserver$', r'kube-controller-manager$', + r'etcd$', r'flanneld$', r'calico-node$', + # 其他高危 + r'svnserve$', r'git-daemon$', r'rsyncd$', r'lighttpd$', r'apache2$', + r'php-fpm$', r'uwsgi$', r'gunicorn$', r'tomcat$', r'java$', r'jvm$', +] + +DEV_TOOL = [ + # 编译器 + r'gcc$', r'g\+\+$', r'clang$', r'clang\+\+$', r'cc$', r'c\+\+$', + r'icc$', r'tcc$', r'pcc$', r'rustc$', r'go$', r'gofmt$', + r'javac$', r'kotlin$', r'scala$', r'groovyc$', + # 链接器/归档器 + r'ld$', r'ld\.bfd$', r'ld\.gold$', r'ld\.lld$', r'ar$', r'nm$', + r'ranlib$', r'strip$', r'objcopy$', r'objdump$', r'readelf$', + r'strings$', r'size$', r'addr2line$', r'c++filt$', + # 构建工具 + r'make$', r'cmake$', r'ninja$', r'meson$', r'autoconf$', r'automake$', + r'libtool$', r'pkg-config$', r'configure$', r'premake$', + # 调试/分析工具 + r'gdb$', r'lldb$', r'valgrind$', r'perf$', r'strace$', r'ltrace$', + r'ptrace$', r'lto-dump$', r'gcov$', r'gcov-dump$', r'gcov-tool$', + r'llvm-.*', r'llc$', r'opt$', r'lli$', + # 预处理 + r'cpp$', r'm4$', r'flex$', r'bison$', r'yacc$', + # 其他开发工具 + r'git$', r'svn$', r'hg$', r'cvs$', r'bzr$', r'fossil$', + r'cargo$', r'npm$', r'yarn$', r'pnpm$', r'pip[0-9]*$', r'gem$', + r'composer$', r'maven$', r'gradle$', r'ant$', r'sbt$', + r'protoc$', r'thrift$', r'grpc_c_plugin$', +] + +DOC_TOOL = [ + # Graphviz + r'dot$', r'neato$', r'circo$', r'fdp$', r'sfdp$', r'twopi$', + r'patchwork$', r'acyclic$', r'gvpr$', r'tred$', r'unflatten$', + r'osage$', r'gml2gv$', r'gv2gml$', r'graphml2gv$', r'mm2gv$', + # 字体/排版 + r'fc-list$', r'fc-pattern$', r'fc-query$', r'fc-scan$', r'fc-match$', + r'fc-cache$', r'fc-cat$', r'fc-conflist$', r'fc-validate$', + r'fc-format$', r'fc-match$', r'fc-query$', + # GTK/GNOME 工具 + r'gsettings$', r'gio$', r'gio-querymodules', r'glib-compile-schemas$', + r'gtk-update-icon-cache$', r'gtk-query-immodules', r'gdk-pixbuf-query-loaders', + # 文档转换 + r'ghostscript$', r'gs$', r'pdftotext$', r'pdfinfo$', r'pdfimages$', + r'dvipdf$', r'ps2pdf$', r'epstopdf$', r'enscript$', + # 其他文档工具 + r'clear$', r'tabs$', r'tput$', r'reset$', r'mesg$', r'wall$', + r'write$', r'banner$', r'figlet$', r'toilet$', + # 图像处理 + r'convert$', r'identify$', r'mogrify$', r'montage$', r'composite$', + r'compare$', r'stream$', r'animate$', r'display$', r'import$', + # 其他 + r'source-highlight', r'highlight$', r'pygmentize$', r'bat$', + r'less$', r'more$', r'most$', r'pg$', r'lv$', +] + +INTERPRETER = [ + # Python + r'python[0-9.]*$', r'python3$', r'python2$', r'pypy[0-9]*$', r'ipython$', + r'pydoc[0-9]*$', r'pdb[0-9]*$', r'pyvenv', r'virtualenv$', + # Perl + r'perl[0-9.]*$', r'cpan$', r'cpanm$', r'perldoc$', + # Ruby + r'ruby[0-9.]*$', r'irb$', r'gem$', r'bundle$', r'rake$', + # Node.js + r'node$', r'nodejs$', r'npm$', r'npx$', r'yarn$', r'pnpm$', + # Shell + r'bash$', r'sh$', r'dash$', r'zsh$', r'fish$', r'ksh$', r'tcsh$', + r'csh$', r'rc$', r'es$', r'xonsh$', r'nushell$', + # Lua + r'lua[0-9.]*$', r'luajit$', r' luarocks$', + # PHP + r'php[0-9]*$', r'php-fpm$', r'composer$', r'pear$', r'pecl$', + # Java + r'java$', r'javaw$', r'jre$', r'jvm$', r'jexec$', + # Tcl/Tk + r'tclsh', r'wish', r'tkcon$', + # 其他 + r'awk$', r'gawk$', r'mawk$', r'nawk$', r'sed$', r'perl$', + r'expect$', r'unbuffer$', r'script$', r'scriptreplay$', +] + +COMPRESS_TOOL = [ + r'tar$', r'gtar$', r'bsdtar$', r'rar$', r'unrar$', r'zip$', r'unzip$', + r'gzip$', r'gunzip$', r'gzexe$', r'zcat$', r'zdiff$', r'zgrep$', + r'bzip2$', r'bunzip2$', r'bzcat$', r'bzdiff$', r'bzgrep$', + r'xz$', r'unxz$', r'xzcat$', r'lzma$', r'unlzma$', r'lzcat$', + r'lz4$', r'unlz4$', r'lz4cat$', r'zstd$', r'unzstd$', r'zstdcat$', + r'7z$', r'7za$', r'7zr$', r'lha$', r'arc$', r'arj$', r'cabextract$', + r'cpio$', r'pax$', r'dpkg-deb$', r'rpm2cpio$', r'rpmbuild$', +] + +NETWORK_TOOL = [ + r'curl$', r'wget$', r'nc$', r'ncat$', r'netcat$', r'tcpdump$', + r'tshark$', r'wireshark$', r'dumpcap$', r'ssh$', r'scp$', r'sftp$', + r'rsync$', r'ftp$', r'lftp$', r'tftp$', r'ncftp$', r'inetd$', + r'telnet$', r'openssl$', r's_client$', r's_server$', r'nmap$', + r'hping3$', r' masscan$', r'zmap$', r'arp-scan$', r'ettercap$', + r'dsniff$', r'tcpflow$', r'ngrep$', r'iptstate$', r'conntrack$', +] + +TEXT_TOOL = [ + r'vim$', r'vi$', r'nvim$', r'emacs$', r'nano$', r'pico$', r'jed$', + r'joe$', r'mcedit$', r'gedit$', r'kate$', r'leafpad$', r'mousepad$', + r'awk$', r'gawk$', r'mawk$', r'nawk$', r'sed$', r'grep$', r'egrep$', + r'fgrep$', r'rg$', r'ag$', r'pt$', r'ack$', r'jq$', r'yq$', + r'cut$', r'paste$', r'join$', r'sort$', r'uniq$', r'wc$', r'head$', + r'tail$', r'cat$', r'tac$', r'nl$', r'od$', r'hexdump$', r'xxd$', + r'column$', r'csplit$', r'fold$', r'fmt$', r'pr$', r'expand$', + r'unexpand$', r'center$', r'par$', +] + +def get_level(name, checks): + missing = [] + if checks.get('relro') != 'Full RELRO': + missing.append('relro') + if checks.get('canary') != 'Canary Found': + missing.append('canary') + if checks.get('nx') != 'NX enabled': + missing.append('nx') + if checks.get('pie') != 'PIE Enabled': + missing.append('pie') + if checks.get('fortify_source') != 'Yes': + missing.append('fortify') + + if not missing: + return 0 + + basename = name.split('/')[-1] + + is_high = any(re.search(p, basename) for p in HIGH_RISK) + is_dev = any(re.search(p, basename) for p in DEV_TOOL) + is_doc = any(re.search(p, basename) for p in DOC_TOOL) + is_interp = any(re.search(p, basename) for p in INTERPRETER) + is_compress = any(re.search(p, basename) for p in COMPRESS_TOOL) + is_network = any(re.search(p, basename) for p in NETWORK_TOOL) + is_text = any(re.search(p, basename) for p in TEXT_TOOL) + + # 高危:特权/网络服务/系统服务 + if is_high: + return 3 + + # 解释器:仅缺失 pie/fortify 视为通过(运行时特性) + if is_interp and len(missing) == 1 and missing[0] in ('pie', 'fortify'): + return 0 + + # 压缩工具:仅缺失 canary 为低危 + if is_compress and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 网络工具:仅缺失 canary/fortify 为中危 + if is_network and len(missing) <= 2 and all(m in ('canary', 'fortify') for m in missing): + return 2 + + # 文本工具:仅缺失 canary 为低危 + if is_text and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 开发工具:仅缺失 pie/fortify 视为通过 + if is_dev and len(missing) == 1 and missing[0] in ('pie', 'fortify'): + return 0 + + # 文档工具:仅缺失 canary 为低危 + if is_doc and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 缺失 >= 2 项为中危 + if len(missing) >= 2: + return 2 + + # 仅缺失 1 项为低危 + return 1 + + +fail_items = [] + +for item in data: + name = item['name'] + level = get_level(name, item['checks']) + + if level > 0: + fail_items.append(f"{name} fail {level}") + +if fail_items: + for line in fail_items: + out.write(line + '\n') +else: + out.write(dirname + ' pass\n') + +out.close() +PYEOF + + echo "结果已保存: $OUTPUT_FILE" + cat "$OUTPUT_FILE" +} + + +echo "安装checksec" +pkginstall + +echo "二进制加固验证" +binary_hardening_test + +echo "结果转为lava result格式数据" +binary_risk_lava "$BRES" "/bin" +binary_risk_lava "$UBRES" "/usr/bin" +binary_risk_lava "$SBRES" "/sbin" +binary_risk_lava "$USBRES" "/usr/sbin" + + + + diff --git a/lava-testcases/security-test/checksec_binary/checksec_binary.yaml b/lava-testcases/security-test/checksec_binary/checksec_binary.yaml new file mode 100644 index 0000000..3f61610 --- /dev/null +++ b/lava-testcases/security-test/checksec_binary/checksec_binary.yaml @@ -0,0 +1,21 @@ +metadata: + name: checksec_binary + format: "Lava-Test Test Definition 1.0" + description: "Run fio on RISC-V device" + maintainer: + - zhangju@iscas.ac.cn + os: + - openEuler-riscv64 + scope: + - security + devices: + - qemu + - lpi4a + - sg2042 + - spacemit-k1-bananapi-f3 +run: + steps: + - cd lava-testcases/security-test/checksec_binary + - bash checksec_binary.sh + - chmod +x ../../utils/send-to-lava.sh + - ../../utils/send-to-lava.sh ./output/result.txt diff --git a/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh b/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh new file mode 100644 index 0000000..cadcabd --- /dev/null +++ b/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +set -x + +OUTPUT="$(pwd)/output" +mkdir -p "$OUTPUT" +RESULT_FILE="${OUTPUT}/result.txt" +KRES="kres.json" + +# 安装checksec +pkginstall(){ + dnf install -y git go + git clone https://github.com/slimm609/checksec.sh.git && cd checksec.sh + go build -o checksec main.go + chmod +x /usr/local/bin/checksec +} + +# 内核加固验证 +kernel_hardening_test(){ + checksec kernel -o json >> $KRES +} + + +kernel_risk_lava(){ + # 内核加固验证:全部通过输出kernel pass,否则输出 配置项名称 + fail + 风险等级 + JSON_FILE="${1}" + OUTPUT_FILE="${2:-$RESULT_FILE}" + + [ ! -f "$JSON_FILE" ] && { echo "Usage: $0 [output_file]"; exit 1; } + + python3 - "$JSON_FILE" "$OUTPUT_FILE" << 'PYEOF' +import json +import sys + +with open(sys.argv[1]) as f: + data = json.load(f) + +output_file = sys.argv[2] +# 配置项: (安全值, 风险等级) +# 等级: 3=高危, 2=中危, 1=低危 +CHECK_RULES = { + # 高危 + 'CONFIG_STACKPROTECTOR': ('Enabled', 3), + 'CONFIG_STACKPROTECTOR_STRONG': ('Enabled', 3), + 'CONFIG_STRICT_KERNEL_RWX': ('Enabled', 3), + 'CONFIG_STRICT_MODULE_RWX': ('Enabled', 3), + 'CONFIG_HARDENED_USERCOPY': ('Enabled', 3), + 'CONFIG_RANDOMIZE_BASE': ('Enabled', 3), + 'CONFIG_SLAB_FREELIST_RANDOM': ('Enabled', 3), + 'CONFIG_SYN_COOKIES': ('Enabled', 3), + 'CONFIG_SECCOMP': ('Enabled', 3), + 'CONFIG_SECCOMP_FILTER': ('Enabled', 3), + 'CONFIG_VMAP_STACK': ('Enabled', 3), + 'CONFIG_FORTIFY_SOURCE': ('Enabled', 3), + 'CONFIG_STRICT_DEVMEM': ('Enabled', 3), + 'CONFIG_IO_STRICT_DEVMEM': ('Enabled', 3), +# 'kernel.randomize_va_space': ('Enabled', 3), 移除该项检查需要实际值为3才正常 + 'kernel.kptr_restrict': ('Enabled', 3), + 'kernel.yama.ptrace_scope': ('Enabled', 3), + 'kernel.kexec_load_disabled': ('Enabled', 3), + 'net.core.bpf_jit_harden': ('Enabled', 3), + 'vm.unprivileged_userfaultfd': ('Enabled', 3), + + # 中危 + 'CONFIG_SECURITY': ('Enabled', 2), + 'CONFIG_SECURITY_SELINUX': ('Enabled', 2), + 'CONFIG_SECURITY_LOCKDOWN_LSM': ('Enabled', 2), + 'CONFIG_SECURITY_LOCKDOWN_LSM_EARLY': ('Enabled', 2), + 'CONFIG_SECURITY_YAMA': ('Enabled', 2), + 'CONFIG_LIST_HARDENED': ('Enabled', 2), + 'CONFIG_DEBUG_LIST': ('Enabled', 2), + 'SELinux': ('Enabled', 2), + 'CONFIG_DEBUG_WX': ('Enabled', 2), + 'CONFIG_DEBUG_SG': ('Enabled', 2), + 'CONFIG_DEBUG_NOTIFIERS': ('Enabled', 2), + 'CONFIG_DEBUG_VIRTUAL': ('Enabled', 2), + 'CONFIG_SCHED_STACK_END_CHECK': ('Enabled', 2), + 'CONFIG_SECURITY_LANDLOCK': ('Enabled', 2), + 'kernel.unprivileged_bpf_disabled': ('Enabled', 2), + 'kernel.dmesg_restrict': ('Enabled', 2), + 'fs.protected_symlinks': ('Enabled', 2), + 'fs.protected_hardlinks': ('Enabled', 2), + 'fs.protected_fifos': ('Enabled', 2), + 'fs.protected_regular': ('Enabled', 2), + + # 低危 + 'CONFIG_LDISC_AUTOLOAD': ('Disabled', 1), + 'CONFIG_SECURITY_SELINUX_BOOTPARAM': ('Disabled', 1), + 'CONFIG_SECURITY_SELINUX_DEVELOP': ('Disabled', 1), + 'dev.tty.ldisc_autoload': ('Disabled', 1), + 'dev.tty.legacy_tiocsti': ('Disabled', 1), + 'kernel.perf_event_paranoid': ('Enabled', 1), +} + +fail_items = [] + +for item in data: + name = item['name'] + value = item['value'] + + if name not in CHECK_RULES: + continue + + expect, level = CHECK_RULES[name] + + passed = False + if expect == 'Enabled' and value == 'Enabled': + passed = True + elif expect == 'Disabled' and value == 'Disabled': + passed = True + + if value in ('Partial', 'Unknown'): + passed = False + if value == 'Unknown': + level = 1 + + if not passed: + display = name + if name == 'vm.unprivileged_userfaultfd': + display = 'vm.unprivileged_userfaultfd (应禁用)' + fail_items.append((display, value, expect, level)) + +with open(output_file, 'w') as out: + if not fail_items: + out.write('kernel pass\n') + else: + fail_items.sort(key=lambda x: -x[3]) + for name, value, expect, level in fail_items: + out.write(f'{name} fail {level}\n') +PYEOF + + # 补充 ASLR 实际值检查 + ASLR_VALUE=$(cat /proc/sys/kernel/randomize_va_space 2>/dev/null || echo "unknown") + + if [ "$ASLR_VALUE" != "2" ]; then + ASLR_MSG="kernel.randomize_va_space_actual fail 3 (当前值: $ASLR_VALUE, 期望: 2)" + echo "$ASLR_MSG" >> "$OUTPUT_FILE" + fi + + echo "结果已保存: $OUTPUT_FILE" + cat "$OUTPUT_FILE" +} + +echo "安装checksec" +pkginstall + +echo "内核加固验证" +kernel_hardening_test + +echo "结果转为lava result格式数据" +kernel_risk_lava "$KRES" + + + + diff --git a/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml b/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml new file mode 100644 index 0000000..4c26f86 --- /dev/null +++ b/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml @@ -0,0 +1,21 @@ +metadata: + name: checksec_kernel + format: "Lava-Test Test Definition 1.0" + description: "Run fio on RISC-V device" + maintainer: + - zhangju@iscas.ac.cn + os: + - openEuler-riscv64 + scope: + - security + devices: + - qemu + - lpi4a + - sg2042 + - spacemit-k1-bananapi-f3 +run: + steps: + - cd lava-testcases/security-test/checksec_kernel + - bash checksec_kernel.sh + - chmod +x ../../utils/send-to-lava.sh + - ../../utils/send-to-lava.sh ./output/result.txt From 620006c60775953116c4fd11c6469f15ce627bfa Mon Sep 17 00:00:00 2001 From: zhangzhang Date: Fri, 12 Jun 2026 15:18:09 +0800 Subject: [PATCH 2/2] checksec --- .../checksec_binary/checksec_binary.sh | 306 ++++++++++++++++++ .../checksec_binary/checksec_binary.yaml | 21 ++ .../checksec_kernel/checksec_kernel.sh | 156 +++++++++ .../checksec_kernel/checksec_kernel.yaml | 21 ++ 4 files changed, 504 insertions(+) create mode 100644 lava-testcases/security-test/checksec_binary/checksec_binary.sh create mode 100644 lava-testcases/security-test/checksec_binary/checksec_binary.yaml create mode 100644 lava-testcases/security-test/checksec_kernel/checksec_kernel.sh create mode 100644 lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml diff --git a/lava-testcases/security-test/checksec_binary/checksec_binary.sh b/lava-testcases/security-test/checksec_binary/checksec_binary.sh new file mode 100644 index 0000000..b556e62 --- /dev/null +++ b/lava-testcases/security-test/checksec_binary/checksec_binary.sh @@ -0,0 +1,306 @@ +#!/bin/bash + +set -x + +OUTPUT="$(pwd)/output" +mkdir -p "$OUTPUT" +RESULT_FILE="${OUTPUT}/result.txt" +BRES="bres.json" # 存放目录/bin的二进制加固结果 +UBRES="ubres.json" # 存放目录/usr/bin的二进制加固结果 +SBRES="sbres.json" # 存放目录/sbin的二进制加固结果 +USBRES="usbres.json" # 存放目录/usr/sbin的二进制加固结果 +RESDIR=() +SCANDIR=() + +# 安装checksec +pkginstall(){ + dnf install -y git go + git clone https://github.com/slimm609/checksec.sh.git && cd checksec.sh + go build -o checksec main.go + cp checksec /usr/local/bin/checksec + chmod +x /usr/local/bin/checksec +} + +# 二进制加固验证 +binary_hardening_test(){ + # 检查 /bin /sbin /usr/bin /usr/sbin + if [ -L "/bin" ] && [ -L "/sbin" ]; then + echo "[*] 检测到 UsrMerge: /bin -> $(readlink /bin), /sbin -> $(readlink /sbin)" + echo "[*] 仅扫描真实路径: /usr/bin, /usr/sbin" + RESDIR=($UBRES $USBRES) + SCANDIR=(/usr/bin /usr/sbin) + else + echo "[*] 未检测到 UsrMerge,使用传统四目录布局" + echo "[*] 扫描完整路径: /bin, /sbin, /usr/bin, /usr/sbin" + RESDIR=($BRES $SBRES $UBRES $USBRES) + SCANDIR=(/bin /sbin /usr/bin /usr/sbin) + fi + if [ "${#SCANDIR[@]}" -ne "${#RESDIR[@]}" ]; then + echo "[!] 错误: SCANDIR(${#SCANDIR[@]}) 与 RESDIR(${#RESDIR[@]}) 数量不匹配!" + exit 1 + fi + for i in "${!SCANDIR[@]}"; do + scan_path="${SCANDIR[$i]}" + res_file="${RESDIR[$i]}" + echo "[*] 正在扫描目录: $scan_path" + echo "[*] 结果输出至: $res_file" + checksec dir "$scan_path" -o json > "$res_file" + done + +} + +# 处理验证结果为lava格式 +binary_risk_lava(){ + JSON_FILE="${1}" + DIRNAME="${2}" + OUTPUT_FILE="${3:-$RESULT_FILE}" + + [ ! -f "$JSON_FILE" ] && { echo "Usage: $0 [output_file]"; exit 1; } + + python3 - "$JSON_FILE" "$DIRNAME" "$OUTPUT_FILE" << 'PYEOF' +import json +import sys +import re + +with open(sys.argv[1]) as f: + data = json.load(f) +dirname = sys.argv[2] +out = open(sys.argv[3], 'a') + +# 0=INFO(通过), 1=LOW, 2=MEDIUM, 3=HIGH +HIGH_RISK = [ + # 特权程序 + r'su(d|do)$', r'passw(d|ord)$', r'login$', r'mount$', r'umount$', + r'ping$', r'ping6$', r'pkexec$', r'chsh$', r'chfn$', r'newgrp$', + r'gpasswd$', r'crontab$', r'at$', r'batch$', r'fusermount$', + r'ntfs-3g$', r'mount\.nfs$', r'mount\.cifs$', + # 系统服务/守护进程 + r'ssh(d|)$', r'Xorg$', r'Xwayland$', r'dbus-daemon$', r'dbus-broker$', + r'polkitd$', r'rtkit-daemon$', r'accounts-daemon$', r'colord$', + # systemd 核心组件 + r'systemd$', r'systemd-journald$', r'systemd-networkd$', r'systemd-resolved$', + r'systemd-logind$', r'systemd-machined$', r'systemd-timesyncd$', + # 网络服务 + r'NetworkManager$', r'firewalld$', r'auditd$', r'sssd$', r'httpd$', + r'nginx$', r'named$', r'bind$', r'mysqld$', r'mariadbd$', r'postgres$', + r'redis-server$', r'mongod$', r'memcached$', r'vsftpd$', r'proftpd$', + r'smbd$', r'nmbd$', r'rpc\..*', r'telnetd$', r'inetd$', r'xinetd$', + # 容器/虚拟化 + r'dockerd$', r'containerd$', r'containerd-shim$', r'crio$', r'podman$', + r'kubelet$', r'kube-proxy$', r'kube-apiserver$', r'kube-controller-manager$', + r'etcd$', r'flanneld$', r'calico-node$', + # 其他高危 + r'svnserve$', r'git-daemon$', r'rsyncd$', r'lighttpd$', r'apache2$', + r'php-fpm$', r'uwsgi$', r'gunicorn$', r'tomcat$', r'java$', r'jvm$', +] + +DEV_TOOL = [ + # 编译器 + r'gcc$', r'g\+\+$', r'clang$', r'clang\+\+$', r'cc$', r'c\+\+$', + r'icc$', r'tcc$', r'pcc$', r'rustc$', r'go$', r'gofmt$', + r'javac$', r'kotlin$', r'scala$', r'groovyc$', + # 链接器/归档器 + r'ld$', r'ld\.bfd$', r'ld\.gold$', r'ld\.lld$', r'ar$', r'nm$', + r'ranlib$', r'strip$', r'objcopy$', r'objdump$', r'readelf$', + r'strings$', r'size$', r'addr2line$', r'c++filt$', + # 构建工具 + r'make$', r'cmake$', r'ninja$', r'meson$', r'autoconf$', r'automake$', + r'libtool$', r'pkg-config$', r'configure$', r'premake$', + # 调试/分析工具 + r'gdb$', r'lldb$', r'valgrind$', r'perf$', r'strace$', r'ltrace$', + r'ptrace$', r'lto-dump$', r'gcov$', r'gcov-dump$', r'gcov-tool$', + r'llvm-.*', r'llc$', r'opt$', r'lli$', + # 预处理 + r'cpp$', r'm4$', r'flex$', r'bison$', r'yacc$', + # 其他开发工具 + r'git$', r'svn$', r'hg$', r'cvs$', r'bzr$', r'fossil$', + r'cargo$', r'npm$', r'yarn$', r'pnpm$', r'pip[0-9]*$', r'gem$', + r'composer$', r'maven$', r'gradle$', r'ant$', r'sbt$', + r'protoc$', r'thrift$', r'grpc_c_plugin$', +] + +DOC_TOOL = [ + # Graphviz + r'dot$', r'neato$', r'circo$', r'fdp$', r'sfdp$', r'twopi$', + r'patchwork$', r'acyclic$', r'gvpr$', r'tred$', r'unflatten$', + r'osage$', r'gml2gv$', r'gv2gml$', r'graphml2gv$', r'mm2gv$', + # 字体/排版 + r'fc-list$', r'fc-pattern$', r'fc-query$', r'fc-scan$', r'fc-match$', + r'fc-cache$', r'fc-cat$', r'fc-conflist$', r'fc-validate$', + r'fc-format$', r'fc-match$', r'fc-query$', + # GTK/GNOME 工具 + r'gsettings$', r'gio$', r'gio-querymodules', r'glib-compile-schemas$', + r'gtk-update-icon-cache$', r'gtk-query-immodules', r'gdk-pixbuf-query-loaders', + # 文档转换 + r'ghostscript$', r'gs$', r'pdftotext$', r'pdfinfo$', r'pdfimages$', + r'dvipdf$', r'ps2pdf$', r'epstopdf$', r'enscript$', + # 其他文档工具 + r'clear$', r'tabs$', r'tput$', r'reset$', r'mesg$', r'wall$', + r'write$', r'banner$', r'figlet$', r'toilet$', + # 图像处理 + r'convert$', r'identify$', r'mogrify$', r'montage$', r'composite$', + r'compare$', r'stream$', r'animate$', r'display$', r'import$', + # 其他 + r'source-highlight', r'highlight$', r'pygmentize$', r'bat$', + r'less$', r'more$', r'most$', r'pg$', r'lv$', +] + +INTERPRETER = [ + # Python + r'python[0-9.]*$', r'python3$', r'python2$', r'pypy[0-9]*$', r'ipython$', + r'pydoc[0-9]*$', r'pdb[0-9]*$', r'pyvenv', r'virtualenv$', + # Perl + r'perl[0-9.]*$', r'cpan$', r'cpanm$', r'perldoc$', + # Ruby + r'ruby[0-9.]*$', r'irb$', r'gem$', r'bundle$', r'rake$', + # Node.js + r'node$', r'nodejs$', r'npm$', r'npx$', r'yarn$', r'pnpm$', + # Shell + r'bash$', r'sh$', r'dash$', r'zsh$', r'fish$', r'ksh$', r'tcsh$', + r'csh$', r'rc$', r'es$', r'xonsh$', r'nushell$', + # Lua + r'lua[0-9.]*$', r'luajit$', r' luarocks$', + # PHP + r'php[0-9]*$', r'php-fpm$', r'composer$', r'pear$', r'pecl$', + # Java + r'java$', r'javaw$', r'jre$', r'jvm$', r'jexec$', + # Tcl/Tk + r'tclsh', r'wish', r'tkcon$', + # 其他 + r'awk$', r'gawk$', r'mawk$', r'nawk$', r'sed$', r'perl$', + r'expect$', r'unbuffer$', r'script$', r'scriptreplay$', +] + +COMPRESS_TOOL = [ + r'tar$', r'gtar$', r'bsdtar$', r'rar$', r'unrar$', r'zip$', r'unzip$', + r'gzip$', r'gunzip$', r'gzexe$', r'zcat$', r'zdiff$', r'zgrep$', + r'bzip2$', r'bunzip2$', r'bzcat$', r'bzdiff$', r'bzgrep$', + r'xz$', r'unxz$', r'xzcat$', r'lzma$', r'unlzma$', r'lzcat$', + r'lz4$', r'unlz4$', r'lz4cat$', r'zstd$', r'unzstd$', r'zstdcat$', + r'7z$', r'7za$', r'7zr$', r'lha$', r'arc$', r'arj$', r'cabextract$', + r'cpio$', r'pax$', r'dpkg-deb$', r'rpm2cpio$', r'rpmbuild$', +] + +NETWORK_TOOL = [ + r'curl$', r'wget$', r'nc$', r'ncat$', r'netcat$', r'tcpdump$', + r'tshark$', r'wireshark$', r'dumpcap$', r'ssh$', r'scp$', r'sftp$', + r'rsync$', r'ftp$', r'lftp$', r'tftp$', r'ncftp$', r'inetd$', + r'telnet$', r'openssl$', r's_client$', r's_server$', r'nmap$', + r'hping3$', r' masscan$', r'zmap$', r'arp-scan$', r'ettercap$', + r'dsniff$', r'tcpflow$', r'ngrep$', r'iptstate$', r'conntrack$', +] + +TEXT_TOOL = [ + r'vim$', r'vi$', r'nvim$', r'emacs$', r'nano$', r'pico$', r'jed$', + r'joe$', r'mcedit$', r'gedit$', r'kate$', r'leafpad$', r'mousepad$', + r'awk$', r'gawk$', r'mawk$', r'nawk$', r'sed$', r'grep$', r'egrep$', + r'fgrep$', r'rg$', r'ag$', r'pt$', r'ack$', r'jq$', r'yq$', + r'cut$', r'paste$', r'join$', r'sort$', r'uniq$', r'wc$', r'head$', + r'tail$', r'cat$', r'tac$', r'nl$', r'od$', r'hexdump$', r'xxd$', + r'column$', r'csplit$', r'fold$', r'fmt$', r'pr$', r'expand$', + r'unexpand$', r'center$', r'par$', +] + +def get_level(name, checks): + missing = [] + if checks.get('relro') != 'Full RELRO': + missing.append('relro') + if checks.get('canary') != 'Canary Found': + missing.append('canary') + if checks.get('nx') != 'NX enabled': + missing.append('nx') + if checks.get('pie') != 'PIE Enabled': + missing.append('pie') + if checks.get('fortify_source') != 'Yes': + missing.append('fortify') + + if not missing: + return 0 + + basename = name.split('/')[-1] + + is_high = any(re.search(p, basename) for p in HIGH_RISK) + is_dev = any(re.search(p, basename) for p in DEV_TOOL) + is_doc = any(re.search(p, basename) for p in DOC_TOOL) + is_interp = any(re.search(p, basename) for p in INTERPRETER) + is_compress = any(re.search(p, basename) for p in COMPRESS_TOOL) + is_network = any(re.search(p, basename) for p in NETWORK_TOOL) + is_text = any(re.search(p, basename) for p in TEXT_TOOL) + + # 高危:特权/网络服务/系统服务 + if is_high: + return 3 + + # 解释器:仅缺失 pie/fortify 视为通过(运行时特性) + if is_interp and len(missing) == 1 and missing[0] in ('pie', 'fortify'): + return 0 + + # 压缩工具:仅缺失 canary 为低危 + if is_compress and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 网络工具:仅缺失 canary/fortify 为中危 + if is_network and len(missing) <= 2 and all(m in ('canary', 'fortify') for m in missing): + return 2 + + # 文本工具:仅缺失 canary 为低危 + if is_text and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 开发工具:仅缺失 pie/fortify 视为通过 + if is_dev and len(missing) == 1 and missing[0] in ('pie', 'fortify'): + return 0 + + # 文档工具:仅缺失 canary 为低危 + if is_doc and len(missing) == 1 and missing[0] == 'canary': + return 1 + + # 缺失 >= 2 项为中危 + if len(missing) >= 2: + return 2 + + # 仅缺失 1 项为低危 + return 1 + + +fail_items = [] + +for item in data: + name = item['name'] + level = get_level(name, item['checks']) + + if level > 0: + fail_items.append(f"{name} fail {level}") + +if fail_items: + for line in fail_items: + out.write(line + '\n') +else: + out.write(dirname + ' pass\n') + +out.close() +PYEOF + + echo "结果已保存: $OUTPUT_FILE" + cat "$OUTPUT_FILE" +} + + +echo "安装checksec" +pkginstall + +echo "二进制加固验证" +binary_hardening_test + +echo "结果转为lava result格式数据" +for i in "${!SCANDIR[@]}"; do + scan_path="${SCANDIR[$i]}" + res_file="${RESDIR[$i]}" + + echo "[*] 扫描目录: $scan_path" + echo "[*] 结果存放路径: $res_file" + binary_risk_lava "$res_file" "$scan_path" +done + + + diff --git a/lava-testcases/security-test/checksec_binary/checksec_binary.yaml b/lava-testcases/security-test/checksec_binary/checksec_binary.yaml new file mode 100644 index 0000000..3f61610 --- /dev/null +++ b/lava-testcases/security-test/checksec_binary/checksec_binary.yaml @@ -0,0 +1,21 @@ +metadata: + name: checksec_binary + format: "Lava-Test Test Definition 1.0" + description: "Run fio on RISC-V device" + maintainer: + - zhangju@iscas.ac.cn + os: + - openEuler-riscv64 + scope: + - security + devices: + - qemu + - lpi4a + - sg2042 + - spacemit-k1-bananapi-f3 +run: + steps: + - cd lava-testcases/security-test/checksec_binary + - bash checksec_binary.sh + - chmod +x ../../utils/send-to-lava.sh + - ../../utils/send-to-lava.sh ./output/result.txt diff --git a/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh b/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh new file mode 100644 index 0000000..75177e7 --- /dev/null +++ b/lava-testcases/security-test/checksec_kernel/checksec_kernel.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +set -x + +OUTPUT="$(pwd)/output" +mkdir -p "$OUTPUT" +RESULT_FILE="${OUTPUT}/result.txt" +KRES="kres.json" + +# 安装checksec +pkginstall(){ + dnf install -y git go + git clone https://github.com/slimm609/checksec.sh.git && cd checksec.sh + go build -o checksec main.go + cp checksec /usr/local/bin/checksec + chmod +x /usr/local/bin/checksec +} + +# 内核加固验证 +kernel_hardening_test(){ + checksec kernel -o json >> $KRES +} + + +kernel_risk_lava(){ + # 内核加固验证:全部通过输出kernel pass,否则输出 配置项名称 + fail + 风险等级 + JSON_FILE="${1}" + OUTPUT_FILE="${2:-$RESULT_FILE}" + + [ ! -f "$JSON_FILE" ] && { echo "Usage: $0 [output_file]"; exit 1; } + + python3 - "$JSON_FILE" "$OUTPUT_FILE" << 'PYEOF' +import json +import sys + +with open(sys.argv[1]) as f: + data = json.load(f) + +output_file = sys.argv[2] +# 配置项: (安全值, 风险等级) +# 等级: 3=高危, 2=中危, 1=低危 +CHECK_RULES = { + # 高危 + 'CONFIG_STACKPROTECTOR': ('Enabled', 3), + 'CONFIG_STACKPROTECTOR_STRONG': ('Enabled', 3), + 'CONFIG_STRICT_KERNEL_RWX': ('Enabled', 3), + 'CONFIG_STRICT_MODULE_RWX': ('Enabled', 3), + 'CONFIG_HARDENED_USERCOPY': ('Enabled', 3), + 'CONFIG_RANDOMIZE_BASE': ('Enabled', 3), + 'CONFIG_SLAB_FREELIST_RANDOM': ('Enabled', 3), + 'CONFIG_SYN_COOKIES': ('Enabled', 3), + 'CONFIG_SECCOMP': ('Enabled', 3), + 'CONFIG_SECCOMP_FILTER': ('Enabled', 3), + 'CONFIG_VMAP_STACK': ('Enabled', 3), + 'CONFIG_FORTIFY_SOURCE': ('Enabled', 3), + 'CONFIG_STRICT_DEVMEM': ('Enabled', 3), + 'CONFIG_IO_STRICT_DEVMEM': ('Enabled', 3), +# 'kernel.randomize_va_space': ('Enabled', 3), 移除该项检查需要实际值为3才正常 + 'kernel.kptr_restrict': ('Enabled', 3), + 'kernel.yama.ptrace_scope': ('Enabled', 3), + 'kernel.kexec_load_disabled': ('Enabled', 3), + 'net.core.bpf_jit_harden': ('Enabled', 3), + 'vm.unprivileged_userfaultfd': ('Enabled', 3), + + # 中危 + 'CONFIG_SECURITY': ('Enabled', 2), + 'CONFIG_SECURITY_SELINUX': ('Enabled', 2), + 'CONFIG_SECURITY_LOCKDOWN_LSM': ('Enabled', 2), + 'CONFIG_SECURITY_LOCKDOWN_LSM_EARLY': ('Enabled', 2), + 'CONFIG_SECURITY_YAMA': ('Enabled', 2), + 'CONFIG_LIST_HARDENED': ('Enabled', 2), + 'CONFIG_DEBUG_LIST': ('Enabled', 2), + 'SELinux': ('Enabled', 2), + 'CONFIG_DEBUG_WX': ('Enabled', 2), + 'CONFIG_DEBUG_SG': ('Enabled', 2), + 'CONFIG_DEBUG_NOTIFIERS': ('Enabled', 2), + 'CONFIG_DEBUG_VIRTUAL': ('Enabled', 2), + 'CONFIG_SCHED_STACK_END_CHECK': ('Enabled', 2), + 'CONFIG_SECURITY_LANDLOCK': ('Enabled', 2), + 'kernel.unprivileged_bpf_disabled': ('Enabled', 2), + 'kernel.dmesg_restrict': ('Enabled', 2), + 'fs.protected_symlinks': ('Enabled', 2), + 'fs.protected_hardlinks': ('Enabled', 2), + 'fs.protected_fifos': ('Enabled', 2), + 'fs.protected_regular': ('Enabled', 2), + + # 低危 + 'CONFIG_LDISC_AUTOLOAD': ('Disabled', 1), + 'CONFIG_SECURITY_SELINUX_BOOTPARAM': ('Disabled', 1), + 'CONFIG_SECURITY_SELINUX_DEVELOP': ('Disabled', 1), + 'dev.tty.ldisc_autoload': ('Disabled', 1), + 'dev.tty.legacy_tiocsti': ('Disabled', 1), + 'kernel.perf_event_paranoid': ('Enabled', 1), +} + +fail_items = [] + +for item in data: + name = item['name'] + value = item['value'] + + if name not in CHECK_RULES: + continue + + expect, level = CHECK_RULES[name] + + passed = False + if expect == 'Enabled' and value == 'Enabled': + passed = True + elif expect == 'Disabled' and value == 'Disabled': + passed = True + + if value in ('Partial', 'Unknown'): + passed = False + if value == 'Unknown': + level = 1 + + if not passed: + display = name + if name == 'vm.unprivileged_userfaultfd': + display = 'vm.unprivileged_userfaultfd (应禁用)' + fail_items.append((display, value, expect, level)) + +with open(output_file, 'w') as out: + if not fail_items: + out.write('kernel pass\n') + else: + fail_items.sort(key=lambda x: -x[3]) + for name, value, expect, level in fail_items: + out.write(f'{name} fail {level}\n') +PYEOF + + # 补充 ASLR 实际值检查 + ASLR_VALUE=$(cat /proc/sys/kernel/randomize_va_space 2>/dev/null || echo "unknown") + + if [ "$ASLR_VALUE" != "2" ]; then + ASLR_MSG="kernel.randomize_va_space_actual fail 3 (当前值: $ASLR_VALUE, 期望: 2)" + echo "$ASLR_MSG" >> "$OUTPUT_FILE" + fi + + echo "结果已保存: $OUTPUT_FILE" + cat "$OUTPUT_FILE" +} + +echo "安装checksec" +pkginstall + +echo "内核加固验证" +kernel_hardening_test + +echo "结果转为lava result格式数据" +kernel_risk_lava "$KRES" + + + + diff --git a/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml b/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml new file mode 100644 index 0000000..4c26f86 --- /dev/null +++ b/lava-testcases/security-test/checksec_kernel/checksec_kernel.yaml @@ -0,0 +1,21 @@ +metadata: + name: checksec_kernel + format: "Lava-Test Test Definition 1.0" + description: "Run fio on RISC-V device" + maintainer: + - zhangju@iscas.ac.cn + os: + - openEuler-riscv64 + scope: + - security + devices: + - qemu + - lpi4a + - sg2042 + - spacemit-k1-bananapi-f3 +run: + steps: + - cd lava-testcases/security-test/checksec_kernel + - bash checksec_kernel.sh + - chmod +x ../../utils/send-to-lava.sh + - ../../utils/send-to-lava.sh ./output/result.txt