可执行性检查¶
AT_EXECVE_CHECK execveat(2) 标志,以及 SECBIT_EXEC_RESTRICT_FILE 和 SECBIT_EXEC_DENY_INTERACTIVE 安全位旨在供脚本解释器和动态链接器使用,以强制执行由内核处理的一致执行安全策略。请参见 samples/check-exec/inc.c 示例。
解释器是否应该检查这些安全位,取决于在执行环境中运行恶意脚本的安全风险,以及内核能否检查脚本是否值得信赖。例如,在服务器上运行的 Python 脚本可以使用任意系统调用并访问任意文件。因此,此类解释器应该被启用以使用这些安全位,并允许用户定义其安全策略。然而,在网页浏览器中运行的 JavaScript 引擎应该已经处于沙盒中,因此不应能够损害用户的环境。
为定制执行环境(例如加固的 Linux 发行版或封闭的容器镜像)构建的脚本解释器或动态链接器,如果通过其他方式处理向后兼容性(例如通过原子更新确保允许执行所有合法库),则可以使用 AT_EXECVE_CHECK 而无需检查相关的安全位。因此,建议脚本解释器和动态链接器默认在运行时检查安全位,但同时也应提供自定义构建的能力,使其表现得如同 SECBIT_EXEC_RESTRICT_FILE 或 SECBIT_EXEC_DENY_INTERACTIVE 始终设为 1(即始终强制执行限制)一样。
AT_EXECVE_CHECK¶
将 AT_EXECVE_CHECK 标志传递给 execveat(2) 仅对常规文件执行检查,如果允许执行此文件则返回 0,同时忽略文件格式以及相关的解释器依赖项(例如 ELF 库、脚本的 shebang)。
程序应始终执行此检查,以便对不由内核直接执行、而是传递给用户空间解释器的文件应用内核级检查。从解释器的角度来看,所有包含可执行代码的文件都应进行检查。然而,此检查的结果应仅根据 SECBIT_EXEC_RESTRICT_FILE 或 SECBIT_EXEC_DENY_INTERACTIVE 进行强制执行。
此标志的主要目的是提高执行环境的安全性和一致性,以确保直接文件执行(例如 ./script.sh)和间接文件执行(例如 sh script.sh)产生相同的结果。例如,这可用于根据调用者的环境检查文件是否值得信赖。
在安全环境中,还应检查库和任何可执行依赖项。例如,动态链接应确保允许执行所有库,以避免简单的绕过(例如使用 LD_PRELOAD)。为了使此类安全执行环境具有意义,只有受信任的代码才是可执行的,这也需要完整性保证。
为了避免导致检查时到使用时(TOCTOU)问题的竞争条件,应将 AT_EXECVE_CHECK 与 AT_EMPTY_PATH 结合使用,以针对文件描述符而不是路径进行检查。
SECBIT_EXEC_RESTRICT_FILE 和 SECBIT_EXEC_DENY_INTERACTIVE¶
当设置了 SECBIT_EXEC_RESTRICT_FILE 时,进程只有在对相关文件描述符和 AT_EXECVE_CHECK 标志调用 execveat(2) 成功时,才能解释或执行文件。
此安全位可以由用户会话管理器、服务管理器、容器运行时、沙盒工具等设置。除测试环境外,还应设置相关的 SECBIT_EXEC_RESTRICT_FILE_LOCKED 位。
程序应仅根据安全位强制执行一致的限制,而不依赖于任何其他用户控制的配置。事实上,这些安全位的使用场景是仅信任经系统配置(通过内核)审查的可执行代码,因此我们必须小心,不要让不受信任的用户控制此配置。
然而,脚本解释器仍可使用用户配置(例如环境变量),只要这不是禁用安全位检查的途径。例如,PATH 和 LD_PRELOAD 变量可以由脚本的调用者设置。更改这些变量可能会导致非预期的代码执行,但仅限于经审查的可执行程序,这是可以接受的。为了使其有意义,系统应提供一致的安全策略以避免任意代码执行,例如通过强制执行写异或执行(W^X)策略。
当设置了 SECBIT_EXEC_DENY_INTERACTIVE 时,进程绝不应解释交互式用户命令(例如脚本)。但是,如果此类命令通过文件描述符(例如 stdin)传递,则如果对相关文件描述符和 AT_EXECVE_CHECK 标志调用 execveat(2) 成功,则应解释其内容。
例如,如果设置了 SECBIT_EXEC_DENY_INTERACTIVE,以脚本片段作为参数调用的脚本解释器应始终拒绝此类执行。
此安全位可以由用户会话管理器、服务管理器、容器运行时、沙盒工具等设置。除测试环境外,还应设置相关的 SECBIT_EXEC_DENY_INTERACTIVE_LOCKED 位。
以下是脚本解释器根据各种 exec 安全位组合的预期行为
SECBIT_EXEC_RESTRICT_FILE=0且SECBIT_EXEC_DENY_INTERACTIVE=0始终解释脚本,并允许任意用户命令(默认)。
没有威胁,每个人和每件事都是受信任的,但由于调用了带有
AT_EXECVE_CHECK的 execveat(2)(该调用应始终执行但被脚本解释器忽略),我们可以防患于未然。事实上,此检查对于使系统管理员能够验证请求(例如通过 audit)并为迁移到安全模式做好准备仍然很重要。SECBIT_EXEC_RESTRICT_FILE=1且SECBIT_EXEC_DENY_INTERACTIVE=0如果脚本不可执行,则拒绝脚本解释,但允许任意用户命令。
威胁是由受信任(且未被欺骗)的用户运行的(潜在)恶意脚本。这可以防止非预期的脚本执行(例如
sh /tmp/*.sh)。这对于(半受限的)用户会话是有意义的。SECBIT_EXEC_RESTRICT_FILE=0且SECBIT_EXEC_DENY_INTERACTIVE=1始终解释脚本,但拒绝任意用户命令。
此用例对于脚本完整性已验证(例如通过 IMA/EVM 或 dm-verity/IPE)但访问权限可能尚未就绪的安全服务(即没有交互式用户会话的服务)可能很有用。事实上,任意交互式命令将更加难以检查。
SECBIT_EXEC_RESTRICT_FILE=1且SECBIT_EXEC_DENY_INTERACTIVE=1如果脚本不可执行,则拒绝脚本解释,同时也拒绝任何任意用户命令。
威胁是不受信任的用户运行的恶意脚本(但代码是受信任的)。这对于可能仅执行受信任脚本的系统服务是有意义的。