2009-07-21 41 views
7

目标:加载的.so或已被验证为被签名(或针对任意的算法验证)可执行的。检查的Linux的签名共享对象之前负载

我希望能够验证一个.so /可执行文件,然后加载/执行一个。所以/可执行文件使用dlopen/...

在这个扳手是,有似乎没有编程办法入住当时的负载。人们可以手动检查文件,然后加载它..但是有一个机会窗口,其中有人可以将该文件换成另一个文件。

我可以想到的一个可能的解决方案是加载二进制文件,检查签名,然后dlopen/execvt /proc/$PID/fd ....但我不知道这是否是一个可行的解决方案。

因为文件系统锁在Linux中是建议性的,所以它对于这个目的不是很有用...(好吧,有mount -o mand ...但是这是用户级而不是根用户的东西)。

+0

貌似整体的问题是无法解决的,而不内核级的干预: -/ 段可重写一次验证...的ptrace可以彻底改变的事情是工作的方式...... 之前,可以做任何事情的答案'神奇的'......看起来像没有root权限和禁用外部调试的方法没有办法做到这一点。 – harningt 2009-07-22 18:17:26

回答

1

的问题是在基本上形成不可解你给了,因为共享对象被装载的mmap()荷兰国际集团到进程内存空间。所以,即使你可以请确保dlopen()的操作上的文件是你检查了一个,并宣布OK,谁可以写入文件可以在修改加载的对象的任何时候你已经后加载它。(这就是为什么你不通过写入来升级运行的二进制文件 - 而是删除然后安装,因为写入它们可能会使正在运行的实例崩溃)。

最好的办法是确保只有你运行的用户可以写入文件,然后检查它,然后dlopen()它。您的用户(或root)仍然可以隐藏不同的代码,但具有这些权限的进程可能只会跟踪()您无论如何都要进行出价。

1

This project理应解决这个内核的水平。

DigSig目前提供:ELF二进制文件和共享库的

  • 运行时签名验证。对于文件的签名吊销
  • 支持。
  • 签名缓存机制来提高性能。
+0

真棒,可悲的是缺少没有root权限的普通用户使用... – harningt 2009-07-21 19:01:50

6

许多动态接头(包括的Glibc的)支持LD_AUDIT环境变量设置为共享库的一个冒号分隔的列表。这些库允许在动态库加载过程中挂钩到各个位置。

#define _GNU_SOURCE 
#include <dlfcn.h> 
#include <link.h> 
unsigned int la_version(unsigned int v) { return v; } 
unsigned int la_objopen(struct link_map *l, Lmid_t lmid, uintptr_t *cookie) { 
    if (!some_custom_check_on_name_and_contents(l->l_name, l->l_addr)) 
     abort(); 
    return 0; 
} 

cc -shared -fPIC -o test.so test.c或其他类似的方法编译。

您可以看到glibc/elf/tst-auditmod1.clatrace的更多示例,或者阅读Linkers and Libraries Guide


非常非常具体到glibc的内部,但你仍然可以挂接到libdl在运行时。

#define _GNU_SOURCE 
#include <dlfcn.h> 
#include <stdio.h> 

extern struct dlfcn_hook { 
    void *(*dlopen)(const char *, int, void *); 
    int (*dlclose)(void *); 
    void *(*dlsym)(void *, const char *, void *); 
    void *(*dlvsym)(void *, const char *, const char *, void *); 
    char *(*dlerror)(void); 
    int (*dladdr)(const void *, Dl_info *); 
    int (*dladdr1)(const void *, Dl_info *, void **, int); 
    int (*dlinfo)(void *, int, void *, void *); 
    void *(*dlmopen)(Lmid_t, const char *, int, void *); 
    void *pad[4]; 
} *_dlfcn_hook; 
static struct dlfcn_hook *old_dlfcn_hook, my_dlfcn_hook; 

static int depth; 
static void enter(void) { if (!depth++) _dlfcn_hook = old_dlfcn_hook; } 
static void leave(void) { if (!--depth) _dlfcn_hook = &my_dlfcn_hook; } 

void *my_dlopen(const char *file, int mode, void *dl_caller) { 
    void *result; 
    fprintf(stderr, "%s(%s, %d, %p)\n", __func__, file, mode, dl_caller); 
    enter(); 
    result = dlopen(file, mode); 
    leave(); 
    return result; 
} 

int my_dlclose(void *handle) { 
    int result; 
    fprintf(stderr, "%s(%p)\n", __func__, handle); 
    enter(); 
    result = dlclose(handle); 
    leave(); 
    return result; 
} 

void *my_dlsym(void *handle, const char *name, void *dl_caller) { 
    void *result; 
    fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle, name, dl_caller); 
    enter(); 
    result = dlsym(handle, name); 
    leave(); 
    return result; 
} 

void *my_dlvsym(void *handle, const char *name, const char *version, void *dl_caller) { 
    void *result; 
    fprintf(stderr, "%s(%p, %s, %s, %p)\n", __func__, handle, name, version, dl_caller); 
    enter(); 
    result = dlvsym(handle, name, version); 
    leave(); 
    return result; 
} 

char *my_dlerror(void) { 
    char *result; 
    fprintf(stderr, "%s()\n", __func__); 
    enter(); 
    result = dlerror(); 
    leave(); 
    return result; 
} 

int my_dladdr(const void *address, Dl_info *info) { 
    int result; 
    fprintf(stderr, "%s(%p, %p)\n", __func__, address, info); 
    enter(); 
    result = dladdr(address, info); 
    leave(); 
    return result; 
} 

int my_dladdr1(const void *address, Dl_info *info, void **extra_info, int flags) { 
    int result; 
    fprintf(stderr, "%s(%p, %p, %p, %d)\n", __func__, address, info, extra_info, flags); 
    enter(); 
    result = dladdr1(address, info, extra_info, flags); 
    leave(); 
    return result; 
} 

int my_dlinfo(void *handle, int request, void *arg, void *dl_caller) { 
    int result; 
    fprintf(stderr, "%s(%p, %d, %p, %p)\n", __func__, handle, request, arg, dl_caller); 
    enter(); 
    result = dlinfo(handle, request, arg); 
    leave(); 
    return result; 
} 

void *my_dlmopen(Lmid_t nsid, const char *file, int mode, void *dl_caller) { 
    void *result; 
    fprintf(stderr, "%s(%lu, %s, %d, %p)\n", __func__, nsid, file, mode, dl_caller); 
    enter(); 
    result = dlmopen(nsid, file, mode); 
    leave(); 
    return result; 
} 

static struct dlfcn_hook my_dlfcn_hook = { 
    .dlopen = my_dlopen, 
    .dlclose = my_dlclose, 
    .dlsym = my_dlsym, 
    .dlvsym = my_dlvsym, 
    .dlerror = my_dlerror, 
    .dladdr = my_dladdr, 
    .dlinfo = my_dlinfo, 
    .dlmopen = my_dlmopen, 
    .pad  = {0, 0, 0, 0}, 
}; 

__attribute__((constructor)) 
static void init(void) { 
    old_dlfcn_hook = _dlfcn_hook; 
    _dlfcn_hook = &my_dlfcn_hook; 
} 

__attribute__((destructor)) 
static void fini(void) { 
    _dlfcn_hook = old_dlfcn_hook; 
} 
 
$ cc -shared -fPIC -o hook.so hook.c 
$ cat > a.c 
#include <dlfcn.h> 
int main() { dlopen("./hook.so", RTLD_LAZY); dlopen("libm.so", RTLD_LAZY); } 
^D 
$ cc -ldl a.c 
$ ./a.out 
my_dlopen(libm.so, 1, 0x80484bd) 

不幸的是,我的调查是导致我的结论是,即使你可以挂接到glibc/elf/dl-load.c:open_verify()(你不能),这是不可能做出对某人这个免费的种族写了段你的图书馆。

+0

甜,这种看起来就像我想要的...除了它是在启动时只检查的环境变量之一: -/ 需要此功能的项目通常作为插件加载到另一个产品中......但LD_AUDIT看起来像是在我们的受控应用程序中使用时可以处理的东西... – harningt 2009-07-21 19:43:14

+0

太棒了!我会给这个最有用的信息标记之一,并将其​​标记为答案,除非有人在理论中直接揭露了一个漏洞。 – harningt 2009-07-22 18:16:38