3 提升 Set-UID 程序的安全性
exec
函数
exec
函数系列通过将当前进程映像包装为新的,来运行紫禁城。有许多exec
函数的版本,工作方式不同。它们可以归类为:
- 使用/不适用 Shell 来启动新程序。
- 通过 Shell 处理命令行参数(Shell 可以引入比我们预期的更多功能。要注意 Shell 是个强大程序)。
- 启动子进程涉及到依赖问题以及属性继承,我们之前看到它可能存在问题。函数
execlp
和execvp
使用 Shell 来启动程序。它们使程序的执行依赖于当前用户安装的 Shell。例如,依赖于PATH
和其它环境变量的值。函数execv
跟家安全,因为它不向代码引入任何这种依赖。 -
system
(CMD)调用向 Shell 传递字符串来执行子进程(即作为单独派生的进程)。它是个用于执行 EXEC 函数的方便的前端。 -
popen
的标准实现也与之相似。这个函数打开到新锦成管道,以便执行命令,并读取任何输出作为文件流。这个函数也启动 Shell 来解释命令字符串。
- 如何安全地调用程序?
- 避免任何 Shell 的调用。不使用
system
,而是execve
:execve
不调用 Shell,system
调用。 - 避免
execlp (file, ...)
和execvp(file,...)
,它们的语义类似于 Shell。它们使用文件内存作为 Shell 的标准输入,如果文件不是有效的可执行目标文件。 - 小心可能使用 Shell 实现的函数。
- Perl 的
open
函数可以执行命令,通常通过 Shell 来这么做。
- 提升
system
的安全性
- 要记住
system
首先调用/bin/sh
。在 Ubuntu 中,它使用参数sh, -c
和用户提供的字符串来调用execv /bin/sh
。 - 在一些 Ubuntu 的早起版本中(例如 9.11),
/bin/sh
(实际上是 Bash)忽略 Set-UID 位选项。因此,在 Set-UID 中调用system
(CMD)时,CMD 不会使用 Root 权限执行,除非 CMD 本身也是个 Set-UID 程序。下面 代码在 Bash 中丢弃了 Set-UID 位。
if (running_setuid && privileged_mode == 0)
disable_priv_mode ();
...
void disable_priv_mode () {
setuid (current_user.uid);
setgid (current_user.gid);
current_user.euid = current_user.uid;
current_user.egid = current_user.gid;
}
- 但是,上面的保护看似破坏了一些需要使用
system
的 Set-UID 程序。因此,从某个版本起,由于添加了其它条件(对于 11.04 和 12.04),保护被移除了。
if (running_setuid && privileged_mode == 0 && act_like_sh ==0)
disable_priv_mode ();
如果 Bash 通过/bin/sh
符号链接调用,act_like_sh
设为 1,因此权限没有禁用。但是,如果你直接将 Bash 变成 Set-UID 程序并尝试运行,保护仍然会有效,并且权限会丢弃。