ELF 中 set-uid 和 INTERP(动态链接器)相对路径的安全问题
ELF 二进制文件的 INTERP 部分中的 set-uid 和相对路径的组合非常危险。
我不太确定应该如何以及在哪里报告这个问题,但在我看来,这就像一个关于 linux/glibc 中动态链接如何工作的一般安全问题,所以让我解释一下它是什么:
考虑构建一个动态链接的二进制文件并在 ELF INTERP 部分中指定相对路径(使用 --dynamic-linker gcc 选项),以便您可以使用动态链接的商业应用程序重新分发自定义 glibc 版本(不允许您静态链接 LGPL glibc,但仍然需要让你的二进制文件在具有不同 glibc 版本的不同 Linux 发行版上工作)。
如果您将二进制文件 chown 为 root,并将 set-uid 标志放在您的二进制文件上,它实际上就变成了 rootkit。由于从不同的目录执行它,允许您替换将以 root 权限执行的动态链接器可执行文件。
为了演示这一点,请看一下以下 C 代码 (issue.c):
#include <stdio.h>
//
// build with:
// gcc -DNAME=\"vulnarable\" -o issue -Wl,--dynamic-linker,.lib64/ld-linux-x86-64.so.2 issue.c
// sudo chown root issue
// sudo chmod u+s issue
// now build some code to be executed with root permissions (we use the same issue.c):
// mkdir -p .lib64/
// gcc -DNAME=\"rootkit\" -o .lib64/ld-linux-x86-64.so.2 --static issue.c
//
int main(int argc, char* argv[])
{
printf("(%s) euid:%d\n", NAME, geteuid());
}
如果您现在像这样执行 set-uid 二进制文件
./issue
,或者只是这样做
ldd issue
而不是得到您可能期望的结果,例如:
(vulnarable) euid:0
您会得到:
(rootkit) euid:0
现在是重点您可以用您喜欢的任何内容替换 ld-linux-x86-64.so.6 二进制文件。
通过不解析 RPATH 中的 $ORIGIN 或在设置了 set-uid 标志时忽略 LD_LIBRARY_PATH ,似乎已经解决了类似的问题。
所以我想知道每当设置 set-uid 标志时是否必须忽略 ELF 中的 INTERP(即通过使用默认动态链接器 - /lib32/ld-linux.so.2 或 /lib64/ld-linux-x86- 64.so.2)?
那么你认为这个问题应该在哪里修复或报告——在 glibc 还是内核中?
The combination of set-uid and a relative path in the INTERP section of an ELF binary is very dangerous.
I'm not quite sure how and where this problem should be reported, but it seems to me like a general security issue concerning how dynamic linking in linux/glibc works, so let me explain what it is:
Consider building a dynamically linked binary and specifying a relative path in the ELF INTERP section (using the --dynamic-linker gcc option) so you could redistribute a custom glibc version with your dynamically linked commercial application (where you are not allowed to link statically against the LGPL glibc, but still need to make your binary work across different linux distribution having different glibc versions).
If you chown the binary to root, and put the set-uid flag on your binary, it effectively becomes a rootkit. As executing it from a different directory, allows you to replace the dynamic linker executable, that will be executed with root permission.
To demonstrate this, take a look at the following C code (issue.c):
#include <stdio.h>
//
// build with:
// gcc -DNAME=\"vulnarable\" -o issue -Wl,--dynamic-linker,.lib64/ld-linux-x86-64.so.2 issue.c
// sudo chown root issue
// sudo chmod u+s issue
// now build some code to be executed with root permissions (we use the same issue.c):
// mkdir -p .lib64/
// gcc -DNAME=\"rootkit\" -o .lib64/ld-linux-x86-64.so.2 --static issue.c
//
int main(int argc, char* argv[])
{
printf("(%s) euid:%d\n", NAME, geteuid());
}
If you now execute the set-uid binary like this
./issue
or even just doing this
ldd issue
instead of getting what you might expect, e.g.:
(vulnarable) euid:0
you get:
(rootkit) euid:0
Now the point is you could replace the ld-linux-x86-64.so.6 binary with whatever you like.
Similar issues seems to have been addressed, by not resolving $ORIGIN in RPATH or ignoring LD_LIBRARY_PATH if the set-uid flag is set.
So I wonder if the INTERP in ELF has to be ignored, whenever the set-uid flag is set (i.e. by using the default dynamic linker - /lib32/ld-linux.so.2 or /lib64/ld-linux-x86-64.so.2)?
So what do you think, where should this be fixed or reported - in glibc or the kernel?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,在非安全位置使用 setuid 二进制文件指定解释器是不安全的(您提到了相对路径,但世界可写的固定路径也有同样的问题)。但这是 ELF 设计的逻辑结论,为 setuid 二进制文件的 INTERP 处理添加特殊情况并不是正确的方法。这是一个“医生,我这样做的时候很痛。 — 不要这样做。” 是的,这种组合不安全,所以不要使用它!无论如何,干预 ELF 解释器是相当高级的事情,因此除非您了解自己在做什么,否则您不应该这样做,在这种情况下,您可以从逻辑上得出什么是安全的或不应该做什么的结论。
ELF/POSIX/Unix/任何东西的高级特性都会给你搬起石头砸自己脚的强大方法,因为它们很强大。如果您想摆脱所有可能的糟糕情况,您的系统将变得不那么灵活,并且更难以编程,同时仍然存在一些漏洞。
Yes, it is not safe to have a setuid binary specifying an interpreter in a non-safe location (you mention relative path, but a world-writable fixed path has the same issue). But it's logical conclusion of the ELF design, and adding a special-case to the handling of INTERP for setuid binaries is not the way to go. It's a case of “Doctor, it hurts when I do this. — Don't do it.” Yes, this combination is unsafe, so simply don't use it! Meddling with the ELF interpreter is something rather advanced anyway, so you shouldn't do it unless you understand what you're doing, in which case you can logically conclude what is safe or not to do.
Advanced features of ELF/POSIX/Unix/whatever give you powerful ways to shoot yourself in the foot, because they are powerful. If you wanted to get rid of every possible bad situation, you would have a system much less flexible and much harder to program for, while still having some holes in it.