如何保护我加载的插件?
How can I secure the plugins I load?
假设我的一段代码扫描目录 ./plugins
并加载带有已知符号(此处为 "function")的 .dll
s/.so
s 以扩展它的功能,像这样:
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 用户身份运行,或具有管理员权限,这意味着任何非特权攻击者都可以通过生成包含名为 known 的函数的共享对象,以特权用户身份执行代码符号(这里,function
)。
如何保护我的 plugins
文件夹?如何检查我加载的共享对象是否安全?
这是 的后续。
我相信在一般情况下您无法保护您的插件。恶意用户可以在其插件中做任意事情(包括在运行时发出一些 C 代码,然后对其进行分叉编译和 dlopen
-ing,JIT 在内存中编译机器代码, ETC...)。请记住 dlopen
-ed(或 mmap
-ed)插件正在共享 virtual address space of the process 运行 它们的加载程序。
我的 MELT [meta-] plugin for the GCC 编译器正是这样做的:在运行时生成 C 或 C++ 代码,即时编译,dlopen
-ing。但它不是恶意的。
请注意(如 ), dlopen(3) is running arbitrary initialization plugin code (before you do any dlsym
to retrieve a function from its "function"
name). Read about __attribute__((constructor))
in GCC.
如果您非常有野心(多年的工作,值得获得博士学位......)您可能会在做一些代码证明或声音时生成插件的代码static analysis 就可以了。
(因为你和我一样在法国巴黎附近,请查看 Xavier Leroy -INRIA- 的作品,例如 CompCert, Emmanuel Chailloux -LIP6-, Julia Lavall & Coccinelle -LIP6-; read also about Frama-C & GCC MELT; but understand that there is no silver bullet.....; 你可以发送给我一封电子邮件,提到你问题的 URL)
另请参阅 halting problem, Rice's theorem, trusted computing base and proof-carrying code. Your request is undecidable or at least intractable。
你可以考虑一些双重方法:有一个 trusted 程序到 "sign" 或 "stamp" 好的插件(所以系统管理员负责告诉某某插件很好),只有 dlopen
是 "certified" 或只是 "human-approved" 的插件。也许就像在受信任的插件名称及其 md5 签名之间保持(受信任和安全的)数据库或文本关联一样简单,并计算校验和然后在 dlopen
...
之前检查它
编程时的一个很好的启发式方法是经常问自己:我是否在尝试解决停机问题? J.Pitrat's blog 有有趣的见解....
在这种情况下你唯一能做的就是只允许特权用户在插件目录中写入文件。
当您对未知文件调用 dlopen
时,您已经丢失了。请注意,您甚至不需要从中调用函数,调用 dlopen
就足够了,因为共享对象可以具有构造函数,无论您是否需要,它们都会自动 运行。
您无法检查共享对象是否安全。这样做就相当于解决了停机问题。
假设我的一段代码扫描目录 ./plugins
并加载带有已知符号(此处为 "function")的 .dll
s/.so
s 以扩展它的功能,像这样:
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 用户身份运行,或具有管理员权限,这意味着任何非特权攻击者都可以通过生成包含名为 known 的函数的共享对象,以特权用户身份执行代码符号(这里,function
)。
如何保护我的 plugins
文件夹?如何检查我加载的共享对象是否安全?
这是
我相信在一般情况下您无法保护您的插件。恶意用户可以在其插件中做任意事情(包括在运行时发出一些 C 代码,然后对其进行分叉编译和 dlopen
-ing,JIT 在内存中编译机器代码, ETC...)。请记住 dlopen
-ed(或 mmap
-ed)插件正在共享 virtual address space of the process 运行 它们的加载程序。
我的 MELT [meta-] plugin for the GCC 编译器正是这样做的:在运行时生成 C 或 C++ 代码,即时编译,dlopen
-ing。但它不是恶意的。
请注意(如 dlsym
to retrieve a function from its "function"
name). Read about __attribute__((constructor))
in GCC.
如果您非常有野心(多年的工作,值得获得博士学位......)您可能会在做一些代码证明或声音时生成插件的代码static analysis 就可以了。
(因为你和我一样在法国巴黎附近,请查看 Xavier Leroy -INRIA- 的作品,例如 CompCert, Emmanuel Chailloux -LIP6-, Julia Lavall & Coccinelle -LIP6-; read also about Frama-C & GCC MELT; but understand that there is no silver bullet.....; 你可以发送给我一封电子邮件,提到你问题的 URL)
另请参阅 halting problem, Rice's theorem, trusted computing base and proof-carrying code. Your request is undecidable or at least intractable。
你可以考虑一些双重方法:有一个 trusted 程序到 "sign" 或 "stamp" 好的插件(所以系统管理员负责告诉某某插件很好),只有 dlopen
是 "certified" 或只是 "human-approved" 的插件。也许就像在受信任的插件名称及其 md5 签名之间保持(受信任和安全的)数据库或文本关联一样简单,并计算校验和然后在 dlopen
...
编程时的一个很好的启发式方法是经常问自己:我是否在尝试解决停机问题? J.Pitrat's blog 有有趣的见解....
在这种情况下你唯一能做的就是只允许特权用户在插件目录中写入文件。
当您对未知文件调用 dlopen
时,您已经丢失了。请注意,您甚至不需要从中调用函数,调用 dlopen
就足够了,因为共享对象可以具有构造函数,无论您是否需要,它们都会自动 运行。
您无法检查共享对象是否安全。这样做就相当于解决了停机问题。