假设我的代码扫描目录./plugins
并加载.dll
s / .so
s,其中包含已知符号(此处为“function”)以扩展其功能,如这样:
的main.c
#include <stdlib.h> #include <dirent.h> #include <string.h> #include <stdio.h> #include <dlfcn.h> int main(void) { DIR *dir; struct dirent *entry; dir = opendir("./plugins"); if (dir == NULL) return -1; while ((entry = readdir(dir)) != NULL) { void *handle; char path[PATH_MAX]; int (*function)(char *); if (strstr(entry->d_name, ".so") == NULL) continue; if (snprintf(path, sizeof(path), "./%s", entry->d_name) >= sizeof(path)) continue; handle = dlopen(path, RTLD_LAZY); if (handle == NULL) continue; // Better: report the error with `dlerror()' function = (int (*)(char *)) dlsym(handle, "function"); if (function != NULL) fprintf(stdout, "function: %d\n", function("example")); else fprintf(stderr, "symbol-not-found: %s\n", entry->d_name); dlclose(handle); } closedir(dir); return 0; }
这可能会导致严重的安全问题:如果我的应用程序以root用户身份运行,或者具有管理员权限,则意味着任何无特权的攻击者都可以通过生成包含名为已知符号的函数的共享对象来作为特权用户执行代码(在这里,function
)。
如何保护plugins
文件夹?如何检查我加载的共享对象是否安全?
这是this question的后续行动。
答案 0 :(得分:3)
在这种情况下,您唯一能做的就是只允许特权用户在插件目录中写入文件。
您在已知丢失的未知文件上调用dlopen
的那一刻。请注意,您甚至不需要从中调用函数,对dlopen
的调用就足够了,因为共享对象可以具有将自动运行的构造函数。
您无法检查共享对象是否安全。要做到这一点等同于解决暂停问题。
答案 1 :(得分:2)
我相信你无法在一般情况下保护你的插件。恶意用户可以在其插件中执行任意事物(包括在运行时发出一些C代码,然后分析它的编译和dlopen
- ing,JIT编译内存中的机器代码等。 )。请注意,dlopen
- ed(或mmap
- ed)插件正在共享virtual address space运行其加载程序的process。
MELT编译器的GCC [meta-]插件正是这样做的:在运行时生成C或C ++代码,动态编译它,dlopen
- 它。但它并不是恶意的。
请注意(作为answered by Art),dlopen(3)正在运行任意初始化插件代码( 之前执行任何dlsym
从其"function"
检索函数1}}名称)。阅读__attribute__((constructor))
in GCC。
如果您非常雄心勃勃(多年的工作,值得一个博士......),您可以在对其进行一些代码验证或声音static analysis时生成插件的代码。
(因为你和我一样在法国巴黎附近,看看Xavier Leroy -INRIA的作品 - 例如CompCert,Emmanuel Chailloux -LIP6-,Julia Lavall&amp; Coccinelle - LIP6-;另请阅读Frama-C&amp; GCC MELT;但要了解有no silver bullet ....;您可以向我发送一封提及该网址的电子邮件你的问题)
另请阅读 halting problem,Rice's theorem,trusted computing base和proof-carrying code 。 您的请求为undecidable 或至少intractable。
你可以考虑一些双重方法:有一个可信程序来“签名”或“盖章”好的插件(所以系统管理员负责告诉这样的插件是好的),以及只有dlopen
一个插件,当它被“认证”或仅仅是“人类认可”时。也许就像在可信插件名称和md5签名之间保持(可信和安全)数据库或文本关联一样简单,并计算校验和,然后在dlopen
之前检查...
编程时的一个好的启发式方法是经常向自己询问:我是否试图解决停止问题? J.Pitrat's blog有一些有趣的见解....