(注意:我想到的是 Linux,但这个问题可能适用于其他平台。)
问题: Linux 不执行 suid #! 脚本也不会在它们上激活“Linux 功能”。
为什么我们会遇到这个问题?因为在内核解释器设置运行脚本期间,攻击者可能已经替换了该文件。如何?以前受信任的 suid/capability-enabled 脚本文件可能位于他可以控制的目录中(例如,可以删除不属于他的受信任文件,或者该文件实际上是他拥有的符号链接)。
正确的解决方案:在以下情况下使内核允许 suid/cap 脚本:a) 很明显调用者对脚本文件没有权力 - 或者 - 像其他几个操作系统一样 b) 将脚本作为 /dev/fd/ 传递x,指的是最初内核打开的可信文件。
我正在寻找的答案:对于无法做到这一点的内核(所有 Linux),我需要一个安全的“现在”解决方案。
我有什么想法?一个二进制包装器,它以安全的方式执行内核不执行的操作。
我想
- 听取已建立的(Python)脚本包装器的消息,这些包装器将 Linux 功能和可能的 suid 从脚本文件传递到解释器以使其有效。
- 获得对我在下面提出的包装器的评论
sudo 的问题:sudo 不是一个好的包装器,因为它无助于内核不陷入刚刚解释的“脚本被替换”陷阱(警告下的“man sudo”是这样说的)。
建议的包装器
- 实际上,我想要一个生成包装器的小程序
- 命令行,例如:sudo suid_capability_wrapper ./script.py
- script.py 已经设置了 suid 位和功能(没有功能,只是信息)
- 生成器 suid_capability_wrapper 确实
- 生成 C(?) 源代码并编译
- 将输出编译为: default:
basename script.py .py
,或参数 -o - 像 script.py 一样设置包装器所有者、组、suid
- 设置允许的功能,如 script.py,忽略可继承和有效的上限
- 如果解释器(例如 /usr/bin/python)在其可继承集中没有相应的大写字母,则发出警告(这是系统限制:没有 suid-root 否则无法传递能力)
- 生成的代码会:
- 检查文件描述符 0、1 和 2 是否打开,否则中止(可能为过于疯狂的环境条件添加更多检查)
- 如果编译的目标脚本是用相对路径编译的,则通过 /proc/self/exe 确定 self 的位置
- 将自己的路径与脚本的相对路径结合起来找到它
- 检查目标脚本所有者、组、权限、大写、suid 是否仍然像原来的(编译的)[这是我想要包括的唯一非必要的安全检查:否则我信任该脚本]
- 将继承的能力集设置为允许的能力集
- execve() 解释器类似于内核所做的,但使用我们知道的脚本路径和我们得到的环境(脚本应该照顾环境)
suid_capability_wrapper 可能会打印一堆注释和警告,以教育用户:
- 确保没有人可以操纵脚本(例如世界可写)
- 请注意 suid/capabilities 来自包装器,没有人关心脚本文件的 suid/xattr 挂载
- 解释器(python)是 execve()ed,它会从这里得到一个肮脏的环境
- 它还将通过它传递标准流程环境的其余部分,即…………(阅读 exec 手册页开始)
- 使用 #!/usr/bin/python -E 使 python 解释器免受环境变量的影响
- 在脚本中自己清理环境,或者注意有很多代码作为副作用运行,它们确实关心其中一些变量