Domanda

Obiettivo:. Carico .so o eseguibile che è stata verificata da firmare (o verificata con un algoritmo arbitrario)

Voglio essere in grado di verificare un .so / eseguibile e quindi caricare / esecuzione che .so / eseguibile con dlopen /...

La chiave di questo è che non sembra esserci alcun modo programmatico per il check-poi-carico. Si potrebbe verificare il file manualmente e poi caricarlo dopo .. Tuttavia c'è una finestra-di-opportunità all'interno del quale qualcuno potrebbe scambiare quel file per un altro.

Una possibile soluzione che mi viene in mente è quello di caricare il file binario, controllare la firma e poi dlopen / execvt il /proc/$PID/fd .... però non so se questo è una soluzione praticabile.

Dal momento che le serrature sono filesystem di consulenza in Linux non sono così utili per questo scopo ... (beh, c'è mount -o mand ... ma questo è qualcosa per livello di utente, non uso root).

È stato utile?

Soluzione

Il problema è essenzialmente insolubile in forma che hai dato, perché gli oggetti condivisi vengono caricati da mmap () ing per elaborare lo spazio di memoria. Quindi, anche se potrebbe fare in modo che il file che dlopen () operato era quello che avevi esaminato e dichiarato OK, chiunque può scrivere sul file può modificare l'oggetto caricato a qualsiasi di tempo dopo aver caricato esso. (Questo è il motivo per cui non si aggiorna esecuzione di binari scrivendo a loro - invece si elimina-then-installato, perché scrivere per loro sarebbe probabilmente in crash qualsiasi istanza).

La cosa migliore è quello di garantire che solo l'utente è in esecuzione come si può scrivere sul file, quindi esaminare, quindi dlopen () esso. Il tuo utente (o root) può ancora intrufolarsi codice diverso, ma processi con quelle autorizzazioni poteva solo ptrace () di fare la loro offerta in ogni caso.

Altri suggerimenti

Molti linker dinamici (compreso di Glibc) sostegno impostando la variabile ambiente LD_AUDIT a una lista separata da due punti di librerie condivise. Queste librerie sono autorizzati a collegare in varie posizioni nel processo di caricamento libreria dinamica.

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

Compila questo con cc -shared -fPIC -o test.so test.c o simili.

Si può vedere glibc/elf/tst-auditmod1.c o latrace per ulteriori esempi, o leggere il linker e delle librerie Guida .


Molto molto specifico per interni di Glibc, ma è ancora possibile collegare in libdl in fase di esecuzione.

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

Purtroppo, le mie indagini stanno portando di concludere che, anche se si potrebbe collegare in glibc/elf/dl-load.c:open_verify() (che non si può), non è possibile fare questo contro qualcuno senza gara scrittura su segmenti della vostra libreria.

Questo progetto risolve presumibilmente questo sul livello del kernel.

  

DigSig attualmente offre:

     
      
  • il tempo di esecuzione firma verifica dei binari ELF e le librerie condivise.
  •   
  • il supporto per la revoca della firma del file.
  •   
  • un meccanismo di firma di caching per migliorare le prestazioni.
  •   

Vi propongo la seguente soluzione che dovrebbe funzionare senza librerie *)

int memfd = memfd_create("for-debugging.library.so", MFD_CLOEXEC | MFD_ALLOW_SEALING);
assert(memfd != -1);

// Use any way to read the library from disk and copy the content into memfd
// e.g. write(2) or ftruncate(2) and mmap(2)
// Important! if you use mmap, you have to unmap it before the next step
// fcntl( , , F_SEAL_WRITE) will fail if there exists a writeable mapping

int seals_to_set = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL;
int sealing_err = fcntl(memfd, F_ADD_SEALS, seals_to_set);
assert(sealing_err == 0);

// Only now verify the contents of the loaded file
// then you can safely *) dlopen("/proc/self/fd/<memfd>");

*) In realtà non testato contro gli attacchi. Non usare in produzione senza ulteriori indagini.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top