Arch 软件包指南/安全
此页面描述了 Arch Linux 软件包的安全打包指南。对于 C/C++ 项目,编译器和链接器可以应用安全加固选项。Arch Linux 默认应用 PIE、FORTIFY_SOURCE、栈保护器、nx 和 relro。
用法
可以通过运行 checksec 来审查加固保护措施。
$ checksec --file=/usr/bin/cat
RELRO
RELRO 是一种通用的缓解技术,用于加固 ELF 二进制/进程的数据段。当程序被加载时,一些 ELF 内存段需要被链接器写入,但在将控制权转交给程序之前可以被设置为只读。这可以防止攻击者覆盖某些 ELF 段。RELRO 有两种不同的模式:
- 部分 RELRO (
-Wl,-z,relro) 在程序加载后,某些段被标记为只读,但 GOT (.got.plt) 仍然是可写的。 - 完整 RELRO (
-Wl,-z,now) 在程序加载期间解析所有动态符号,从而允许整个 GOT 被标记为只读。
如果应用程序报告为部分 RELRO,请调查构建工具链是否传递了我们的 LDFLAGS 或允许覆盖 LDFLAGS。对于 Go 软件包,请调查构建方法是否使用了 build.go 作为纯 golang Makefile 替代品,后者不允许传递 LDFLAGS。
Haskell
目前尚不清楚如何为 Haskell 实现完整的 RELRO。
Go
请参阅 Go 软件包指南#标志和构建选项。
栈保护器
编译器在栈上的缓冲区和控制数据之间插入了一个 栈保护器。如果这个已知值被损坏,则发生了缓冲区溢出,运行的程序会发生段错误,以防止可能的任意代码执行。
带有 --enable-default-ssp 编译选项的 gcc 软件包默认启用了栈保护。
NX
C/C++
可执行空间保护将内存区域标记为不可执行,因此尝试在这些区域执行机器代码将导致异常。它利用了 NX 位(no-execute bit)等硬件功能,或者在某些情况下对这些功能进行软件模拟。
PIE
C/C++
带有 --enable-default-pie 选项的 gcc 软件包默认启用了 C/C++ 的 PIE。
Golang
将以下标志传递给 go build
export GOFLAGS='-buildmode=pie' export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=3" export CGO_LDFLAGS="-Wl,-z,relro,-z,now"
Haskell
将以下标志传递给 runhaskell Setup.hs configure
--ghc-option='-pie'
RPATH/RUNPATH
RUNPATH/RPATH 为其所在的对象的附加搜索路径(可用于可执行文件和共享对象)。
$ objdump -x /usr/bin/perl | grep -E 'RPATH|RUNPATH'
如果 RPATH 值包含攻击者可控的路径,攻击者可以通过在该目录中安装恶意库来执行代码,例如 CVE-2006-1566 CVE-2005-4280。请参阅 Debian:RpathIssue。
RPATH 条目由链接器设置,例如通过将字符串 -Wl,-rpath -Wl,/usr/local/lib 传递给 LDFLAGS。要创建 RUNPATH 条目,请将 --enable-new-dtags 添加到链接器标志中。
FORTIFY
Fortify source 是一个宏,为执行内存和字符串操作的各种函数添加了缓冲区溢出保护。它会检查攻击者是否试图复制超过缓冲区大小的字节,然后停止程序的执行。此保护通过默认的 CPPFLAGS 启用。
makepkg.conf
CPPFLAGS="-D_FORTIFY_SOURCE=3"
请参阅 makepkg#配置。
systemd 服务
如果软件包中包含 systemd 服务文件(因为上游未提供),请考虑应用以下 systemd 服务加固功能。Systemd 提供了一种分析服务启用了哪些安全功能的方法。
$ systemd-analyze security reflector.service
文件访问
可以通过限制文件系统访问来加固服务。
为执行进程设置一个新的文件系统命名空间,并在其中挂载私有的 /tmp 和 var/tmp 目录,这些目录不与命名空间外的进程共享。对于写入 /tmp 的程序很有用。
PrivateTmp=true
ProtectSystem 有三种不同的将目录挂载为只读的变体,用于执行进程。 “full” 选项将 /usr、/boot 和 /etc 挂载为只读。ProtectHome 使执行进程无法访问 /home、/root 和 /run/user。
ProtectSystem=strict ProtectHome=true
为执行进程设置一个新的 /dev 命名空间,并且只添加 API 伪设备,例如 /dev/null、/dev/zero 或 /dev/random,但不包括物理设备或系统内存、系统端口等。这对于保护执行进程免受直接写入物理设备很有用,systemd 还为 @raw-io 集内的调用添加了系统调用过滤器。
PrivateDevices=true
这些选项使执行进程无法更改可通过 /proc/sys、/sys 等访问的内核变量。ProtectControlGroups 使 /sys/fs/cgroup 层级结构变为只读。
ProtectKernelTunables=true ProtectControlGroups=true
可以通过以下方式使文件路径不可访问
InaccessiblePaths=/etc
更多详细信息可以在 systemd.exec(5) 中找到。
用户
确保执行进程及其子进程永远无法通过 execve(2) 获得新的特权。
NoNewPrivileges=true
内存
禁止尝试创建同时可写和可执行的内存映射,禁止更改映射为可执行,或禁止创建可执行的共享内存。这可以防止攻击者写入可执行内存。请注意,启用此功能并非与所有依赖 JIT 的应用程序兼容。
MemoryDenyWriteExecute=true
系统调用
锁定 personality(2) 系统调用,以便无法更改内核执行域。
LockPersonality=true
系统调用也可以在服务中受到限制,systemd 可以显示要过滤的系统调用
$ systemd-analyze syscall-filter
提供了预定义组,例如,要使用推荐的系统服务系统调用白名单起始点,请使用
SystemCallFilter=@system-service
可以通过其架构限制系统调用,例如防止 32 位二进制文件在 64 位机器上执行(不允许非原生二进制文件)
SystemCallArchitectures=native
网络
如果运行的进程不需要任何网络访问,可以通过为进程设置新的网络命名空间并仅配置环回接口来完全禁用它。
PrivateNetwork=true
如果需要网络,可以通过为 socket(2) 系统调用限制使用的地址族类型,例如仅允许 UNIX 套接字。
RestrictAddressFamilies=AF_UNIX
如果只需要与 localhost 或特定 IP 地址范围的网络连接,可以通过仅允许与 localhost 的网络访问来限制进程。
IPAddressAllow=localhost IPAddressDeny=any
有关网络过滤的更多信息可以在 systemd.resource-control(5) 中找到。
杂项
为执行进程设置新的 UTS 命名空间,并禁止更改主机名或域名。
ProtectHostname=true