2009-07-21 23 views
7

Mục tiêu: Tải .so hoặc tệp thực thi đã được xác minh để được ký (hoặc được xác minh dựa trên thuật toán tùy ý).Kiểm tra chữ ký của đối tượng chia sẻ Linux trước khi tải

Tôi muốn để có thể xác minh một .so/thực thi và sau đó tải/thực hiện mà .so/thực thi với dlopen/...

Các chìa khoá ở đây là rằng có vẻ là không có cách nào để chương trình kiểm tra sau đó tải. Người ta có thể kiểm tra các tập tin bằng tay và sau đó tải nó sau khi .. tuy nhiên có một cửa sổ của cơ hội trong đó ai đó có thể trao đổi ra tập tin đó cho người khác.

Một giải pháp khả thi mà tôi có thể nghĩ là tải nhị phân, kiểm tra chữ ký và sau đó dlopen/execvt /proc/$PID/fd .... tuy nhiên tôi không biết nếu đó là một giải pháp khả thi.

Vì ổ khóa hệ thống tập tin được tư vấn trong Linux, chúng không hữu ích cho mục đích này ... (tốt, có mount -o mand ... nhưng đây là một cái gì đó cho userlevel, không sử dụng root).

+0

Hình như vấn đề tổng thể là không thể giải quyết mà không cần sự can thiệp của cấp hạt nhân: -/ Phân đoạn có thể được ghi đè một lần xác minh ... ptrace hoàn toàn có thể thay đổi cách mọi thứ đang làm việc ... Trong khi chờ câu trả lời có thể làm điều gì đó 'huyền diệu' ... có vẻ như không có cách nào để làm điều này mà không có đặc quyền cấp độ gốc và một số cách để vô hiệu hóa gỡ lỗi bên ngoài. – harningt

Trả lời

1

Vấn đề cơ bản là không thể giải quyết được trong biểu mẫu bạn đã cung cấp, vì các đối tượng dùng chung được nạp bởi mmap() ing để xử lý không gian bộ nhớ. Vì vậy, ngay cả khi bạn có thể hãy đảm bảo rằng tệp dlopen() hoạt động là tệp bạn đã kiểm tra và khai báo OK, bất kỳ ai có thể ghi vào tệp có thể sửa đổi đối tượng được tải tại bất kỳ thời gian nào sau khi bạn đã nạp nó.(Đây là lý do tại sao bạn không nâng cấp các tệp nhị phân đang chạy bằng cách viết cho chúng - thay vào đó bạn xóa-sau đó-cài đặt, vì việc viết thư cho chúng có thể sẽ sụp đổ bất kỳ trường hợp đang chạy nào).

Đặt cược tốt nhất của bạn là đảm bảo rằng chỉ người dùng bạn đang chạy có thể ghi vào tệp, sau đó kiểm tra nó, sau đó dlopen() nó. Người dùng của bạn (hoặc root) vẫn có thể lẻn mã khác nhau, nhưng các tiến trình với các điều khoản đó có thể chỉ ptrace() bạn làm việc đặt giá thầu của họ.

+1

Vâng, 'mmap (,,, MAP_COPY ,,)' sẽ cung cấp cho một ánh xạ mà không bị ảnh hưởng bởi những thay đổi thêm vào tệp trên đĩa, nhưng nó không được triển khai rộng rãi. Trên Linux và hầu hết các hệ thống khác, 'mmap (,,, MAP_PRIVATE ,,)' được sử dụng; nó không được xác định bởi POSIX liệu các thay đổi đối với tệp có thay đổi ánh xạ hay không, nhưng nói chung chúng làm trừ khi trang đã được sao chép trên ghi. – ephemient

+0

Ah, tốt bắt trên đó ... và ptrace ám này tất cả vô ích không nó: -/ Làm cho DigSig trông giống như lựa chọn duy nhất ... hoặc một hệ thống tập tin cung cấp truy cập chỉ đọc vào dữ liệu đó là chữ ký đã xác minh ... – harningt

+0

Đánh dấu phần này là 'câu trả lời' vì dường như không có sự thay thế nào ngăn cản người dùng gốc/người dùng hiện tại không bị nhại. – harningt

1

This project được cho là giải quyết vấn đề này ở cấp hạt nhân.

DigSig hiện đang cung cấp:

  • chạy xác minh chữ ký thời điểm nhị phân ELF và các thư viện chia sẻ.
  • hỗ trợ thu hồi chữ ký của tệp.
  • cơ chế lưu bộ nhớ đệm chữ ký để tăng cường hiệu suất.
+0

Tuyệt vời, đáng buồn mất đi sự sử dụng phổ biến của người dùng w/o quyền root ... – harningt

6

Nhiều linkers động (bao gồm cả Glibc của) thiết lập biến môi trường LD_AUDIT vào một danh sách thư đại tràng-tách các thư viện chia sẻ hỗ trợ. Các thư viện này được phép nối vào các vị trí khác nhau trong quá trình tải thư viện động.

#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; 
} 

Biên dịch với cc -shared -fPIC -o test.so test.c hoặc tương tự.

Bạn có thể xem glibc/elf/tst-auditmod1.c hoặc latrace để biết thêm ví dụ hoặc đọc Linkers and Libraries Guide.


Rất cụ thể đối với ruột của Glibc, nhưng bạn vẫn có thể móc vào libdl khi chạy.

#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) 

Thật không may, cuộc điều tra của tôi đang dẫn đầu tôi đến kết luận rằng ngay cả khi bạn có thể móc vào glibc/elf/dl-load.c:open_verify() (mà bạn có thể không), nó không thể làm cho cuộc đua miễn phí này để chống lại ai đó viết trên các phân đoạn của thư viện của bạn.

+0

Ngọt ngào, kiểu này trông giống như những gì tôi muốn ... ngoại trừ một trong những biến môi trường chỉ được kiểm tra lúc khởi động: -/ Dự án cần tính năng này thường được tải dưới dạng plugin cho sản phẩm khác ... tuy nhiên LD_AUDIT trông giống như một thứ hữu ích để xử lý khi được sử dụng trong các ứng dụng được kiểm soát của chúng tôi ... – harningt

+0

Tuyệt vời! Tôi sẽ cung cấp cho nó một trong những thông tin hữu ích nhất và đánh dấu nó là câu trả lời ngoại trừ trường hợp ai đó chọc thủng lỗ hổng trong lý thuyết. – harningt

Các vấn đề liên quan