【问题标题】:How can I secure the plugins I load?如何保护我加载的插件?
【发布时间】:2016-01-11 13:07:37
【问题描述】:

假设我的一段代码扫描目录./plugins 并加载.dlls/.sos 和一个已知符号(此处为“函数”)以扩展其功能,如下所示:

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的后续。

【问题讨论】:

  • 你能解释一下你为什么关心插件安全吗?
  • 这是一个普遍的问题。每个软件开发人员都应该考虑到安全性,无论是其应用程序的安全性还是整个系统的安全性。在这里,由于我的代码,攻击者可能会破坏整个系统。
  • 但是你为什么不相信这个插件呢?
  • 因为任何人都可以将其放入文件夹中,包括恶意攻击者。
  • 那么你的整个方法都是错误的。

标签: c security plugins shared-libraries


【解决方案1】:

在这种情况下,您唯一能做的就是只允许特权用户在插件目录中写入文件。

当你在一个你已经丢失的未知文件上调用dlopen 的那一刻。请注意,您甚至不需要从中调用函数,只需调用dlopen 就足够了,因为共享对象可以具有无论您是否需要都会自动运行的构造函数。

您无法检查共享对象是否安全。这样做就相当于解决了停机问题。

【讨论】:

    【解决方案2】:

    我相信在一般情况下您无法保护您的插件。恶意用户可以在其插件中做任意事情(包括在运行时发出一些 C 代码,然后分叉编译它和dlopen-ing、JIT 在内存中编译机器代码等...) .请记住,dlopen-ed(或mmap-ed)插件正在共享运行其加载程序的processvirtual address space

    我的GCC 编译器的MELT [meta-] 插件正是这样做的:在运行时生成C 或C++ 代码,即时编译它,dlopen-ing 它。但它不是恶意的。

    注意(如answered by Art),dlopen(3) 正在运行任意初始化插件代码(您执行任何dlsym 以从其"function" 名称中检索函数)。阅读__attribute__((constructor)) in GCC

    如果您非常雄心勃勃(工作多年,值得获得博士学位......)您可能会生成插件的代码,同时对其进行一些代码证明或声音 static analysis

    (由于你和我一样靠近法国巴黎,请查看 Xavier Leroy -INRIA- 例如CompCert、Emmanuel Chailloux -LIP6-、Julia Lavall 和 Coccinelle -LIP6- 的作品;另请阅读关于Frama-C & GCC MELT;但请理解no silver bullet....;您可以给我发一封电子邮件,提及您的问题的网址)

    另请阅读halting problemRice's theoremtrusted computing baseproof-carrying code您的请求是 undecidable 或至少是 intractable

    你可以考虑一些双重方法:有一个受信任的程序来“签名”或“标记”好的插件(所以系统管理员负责告诉这样和这样的插件是好的),并且只有dlopen 一个插件,当它以某种方式被“认证”或简单地“人工批准”时。也许就像在受信任的插件名称和它们的 md5 签名之间保持一个(受信任和安全的)数据库或文本关联一样简单,然后计算校验和,然后在 dlopen 之前检查它......

    编程时一个很好的启发式方法是经常问自己:我是否正在尝试解决停机问题? J.Pitrat's blog 有有趣的见解....

    【讨论】:

    • 这是否意味着每次调用dlopen() 都会导致安全问题?!
    • 是的,当然。实际上,拥有任何类型的函数指针已经是一个安全问题
    • 附带说明,验证包含插件的目录是否由受信任的用户和组拥有并且不可全局写入,并且插件文件也由受信任的用户和组拥有,不是世界可写的,并且在打开之前只有一个硬链接,通常就足够了。对于比赛来说,这仍然是可疑的。重命名父目录(尽管您也可以通过检查所有父目录来尝试减轻这种情况)。不过,大多数应用程序(例如 Apache)使用固定的受信任(或“系统”)目录来代替这些检查。
    猜你喜欢
    • 1970-01-01
    • 2011-05-17
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    相关资源
    最近更新 更多