8

目標: 署名されていることが検証された (または任意のアルゴリズムに対して検証された) .so または実行可能ファイルをロードします。

.so/executable を検証してから、その .so/executable を dlopen/... でロード/実行できるようにしたい

これのレンチは、チェックしてからロードするプログラムによる方法がないように見えることです。ファイルを手動でチェックしてからロードすることもできますが、誰かがそのファイルを別のファイルと交換できる機会があります。

私が考えることができる解決策の 1 つは、バイナリをロードし、署名を確認してから dlopen/execvt/proc/$PID/fdを実行することですが、それが実行可能な解決策であるかどうかはわかりません。

ファイルシステムのロックは Linux では推奨事項であるため、この目的にはあまり役に立ちません... (まあ、ありmount -o mandます... しかし、これは root での使用ではなく、ユーザーレベルでの使用です)。

4

4 に答える 4

9

多くのダイナミックリンカ(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これをまたは同様のものとコンパイルします。

その他の例については、latraceを参照glibc/elf/tst-auditmod1.cするか、latraceを参照するか、リンカーとライブラリガイドをお読みください。


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> ac
#include <dlfcn.h>
int main(){dlopen( "./ hook.so"、RTLD_LAZY); dlopen( "libm.so"、RTLD_LAZY); }
^ D
$ cc -ldl ac
$ ./a.out
my_dlopen(libm.so、1、0x80484bd)

残念ながら、私の調査により、たとえあなたがフックすることができたとしてもglibc/elf/dl-load.c:open_verify()(それはできません)、誰かがあなたのライブラリのセグメントを上書きすることに対してこれをレースフリーにすることは不可能であると結論付けました。

于 2009-07-21T19:20:15.467 に答える
2

メモリ空間を処理するために共有オブジェクトが mmap()ing によってロードされるため、問題は本質的にあなたが与えた形式では解決できません。したがって、dlopen() が操作したファイルが、調べて OK と宣言したファイルであることを確認できたとしても、そのファイルに書き込むことができる人なら誰でも、ロードした後はいつでも、ロードされたオブジェクトを変更できます。(これが、実行中のバイナリに書き込みを行ってアップグレードするのではなく、削除してからインストールする理由です。バイナリに書き込むと、実行中のインスタンスがクラッシュする可能性があるためです)。

あなたの最善の策は、あなたが実行しているユーザーだけがファイルに書き込めるようにし、それを調べてから dlopen() することです。あなたのユーザー (または root) は別のコードをこっそり持ち込むことができますが、それらの権限を持つプロセスは ptrace() するだけで、とにかく彼らの入札を行うことができます.

于 2009-07-22T00:54:25.233 に答える
2

このプロジェクトは、おそらくカーネルレベルでこれを解決します。

DigSig は現在、以下を提供しています。

  • ELF バイナリと共有ライブラリのランタイム署名検証。
  • ファイルの署名取り消しのサポート。
  • パフォーマンスを向上させるシグネチャ キャッシング メカニズム。
于 2009-07-21T18:54:10.637 に答える