阅读时这,我发现了以下漏洞:
% cp /usr/bin/id ~
% chmod -x ~/id
% ls -al ~/id
-rw-r--r-- 1 edd edd 22020 2012-08-01 15:06 /home/edd/id
% ~/id
zsh: permission denied: /home/edd/id
% /lib/ld-linux.so.2 ~/id
uid=1001(edd) gid=1001(edd) groups=1001(edd),1002(wheel)
此代码片段表明,我们可以以普通非特权用户的身份轻松绕过文件系统的执行权限。我在 Ubuntu 12.04 上运行了此代码。
虽然根据 file(1),Linux 加载程序是一个共享对象,但它也有一个允许直接执行的入口点。当以这种方式执行时,Linux 加载程序充当 ELF 二进制文件的解释器。
但是,在我的 OpenBSD 机器上,此漏洞无效,因为您可能无法将加载程序作为程序执行。OpenBSD 手册页说:“ld.so 本身是一个共享对象,最初由内核加载。”。
在 Solaris 9 上尝试此操作,您将得到一个段错误。我不确定其他地方会发生什么。
我的问题是:
- 为什么 Linux 加载程序(直接执行时)不是在解释 ELF 二进制文件之前检查文件系统属性?
- 如果可以轻易绕过,为什么要实施禁止执行文件的机制?我是不是漏掉了什么?
答案1
许可的目的execute
是不是阻止执行一般来说。它(1)告诉程序哪些文件需要执行,(2)阻止执行作为特权用户,当指定 setuid 位(等等)时。
链接器攻击并不像看上去那么容易。你可以更轻松地执行任何你有权读取的不可执行文件:
$ cp unexecutable_file ~/runme
$ chmod +x ~/runme
$ ~/runme
总之:
标记应执行的文件
当你编写一个 shell 脚本时,你可以用 将其标记为可执行文件chmod +x
。这向你的 shell 暗示你希望它是可执行的(否则对于所有 shell 来说,它只是另一个纯文本文件)。然后,当你输入 时,shell 可以在制表符补全中显示它./
Tab。
类似地:something.d
目录(例如init.d
)包含通常由守护进程自动执行的启动或控制 shell 脚本。您可能希望将注释或 README 文件作为纯文本文件放在目录中。或者您可能希望暂时禁用其中一个脚本。您可以通过清除该特定文件的执行位来实现这一点。这会告诉守护进程跳过它。
防止特权执行
位setuid
的意思是当你执行该文件时,是以指定的用户身份(例如root)执行的。
论坛帖子解释得很好:
您希望某个可执行文件对某些用户具有 setuid 权限,但您只希望特定组中的用户能够以 setuid 权限执行该文件。他们仍然可以通过复制来执行该文件,但 setuid 标志会丢失,因此他们将以自己的身份执行该文件,而不是以拥有原始文件的用户身份执行该文件。
答案2
如果您具有某个文件的读取权限,那么您可以随时复制它。
如果您可以制作个人副本,则您可以始终将该副本标记为可执行文件。
这并不能解释 ld-linux 的行为但确实表明它可能不是一个非常有用的安全漏洞。
如果您想要更严格的安全性,请考虑SELinux
答案3
换个角度来看这个问题:正如 Mechanical Snail 所说,文件的执行权限并非旨在阻止执行。但是,文件系统选项“noexec”确实会阻止执行,而且不容易被绕过(并非所有文件系统都支持,但最流行的 Linux 文件系统肯定支持)。如果管理员想阻止用户运行自己的程序,他们可以在主目录和 tmp 目录以及用户可能创建文件的任何其他目录中指定 noexec 选项。
$ mount -o noexec /dev/sdd1 /test
$ cd /test
$ cp /usr/bin/id .
$ ./id
-bash: ./id: Permission denied
显然,过去可以使用问题中提到的加载器技巧来绕过 noexec 选项,但是该问题在几个版本之前的内核中已得到修复。
从http://linux.die.net/man/8/mount:
禁止执行
不允许在已挂载的文件系统上直接执行任何二进制文件。(直到最近,仍然可以使用 /lib/ld*.so /mnt/binary 之类的命令来运行二进制文件。此技巧自 Linux 2.4.25 / 2.6.0 起失效。)