2013-08-05 11 views
5

Nel mio ambiente personalizzato è precaricata una libreria di intercettazione che esegue un'implementazione speciale delle chiamate bind(), connect(), ecc.L'utilizzo delle funzionalità di linux disabilita LD_PRELOAD

Il problema che vedo è quando un'applicazione abilita esplicitamente le funzionalità utilizzando il comando setcap, l'esecuzione dell'applicazione non riesce a precaricare la libreria interceptor e chiama la libc predefinita connect().

È un comportamento previsto? Se sì, quale potrebbe essere la ragione per disabilitare LD_PRELOAD?

C'è qualche modifica o metodo che è possibile utilizzare per precaricare correttamente la libreria con le funzionalità abilitate.

+2

Vedere [questa domanda] (http://stackoverflow.com/questions/9843178/linux-capabilities-setcap-seems-to-disable-ld-library-path) per le risposte. – scai

+1

È possibile scrivere un programma wrapper per il binario di destinazione. Sarà più o meno necessario impostare setuid root. Forgerà un processo figlio, quindi eseguirà il binario di destinazione (con il set 'LD_PRELOAD'); il file binario di destinazione non ha alcuna capacità di file impostata. La libreria di precarico comunica quindi con il processo figlio (tramite ad esempio una coppia di socket su say fd 3), con il processo figlio che concede le capacità necessarie al processo di destinazione, quindi esce (e la libreria di precarico raccoglie il bambino). Fammi sapere se vuoi un esempio. –

+0

@NominalAnimal Sono contento se puoi mostrarmi un esempio. –

risposta

4

Come ha risposto Oliver Matthews, LD_PRELOAD è disabilitato per entrambi i binari setuid e per i file binari con capacità di file, per motivi di sicurezza.

Per precaricare una biblioteca, pur consentendo le funzionalità di file, si hanno due opzioni:

  1. Impostare

    (Il linker dinamico Linux ld.so fa precarico librerie anche per setuid/file di libreria precaricata setuid radice binari -capability-enabled, se le librerie sono di proprietà di root e contrassegnati setuid.)

  2. Usa un wrapper setuid root

    012.

    Il wrapper ottiene i privilegi di root completi (zero ID utente e gruppo reali ed effettivi pari a zero) e memorizza l'ID utente e il gruppo originali originali ad es. variabili ambientali).

    La libreria precaricata ha un costruttore, ad es.

    static void my_library_init(void) __attribute__((constructor)); 
    static void my_library_init(void) 
    { 
        /* ... */ 
    } 
    

    che viene eseguito automaticamente prima main() (ma eventualmente dopo altri costruttori in altre librerie precaricati, o librerie che le librerie precaricati dipendono).

    Questo costruttore ottiene le funzionalità desiderate, designate tramite variabili di ambiente (getenv(), cap_from_text()) o il file eseguibile binario stesso (cap_from_file("/proc/self/exe")).

    Il costruttore deve utilizzare temporaneamente prctl(PR_SET_KEEPCAPS, 1) mantenere capacità su un cambio di identità, e trattenere CAP_SETUID e CAP_SETGID capacità di essere in grado di cambiare identità dalla radice per l'utente e gruppo specificato nelle variabili d'ambiente, prima limitandosi alla capacità finale impostato.

Entrambe le opzioni hanno ovvie considerazioni di sicurezza. Raccomando il controllo di integrità (e la cancellazione di LD_PRELOAD) nel costruttore di librerie precaricato. Se qualcosa sembra sospetto, utilizzare _exit() per interrompere immediatamente il processo.

In generale, raccomando la prima opzione per la semplicità (sia per l'implementazione che per i problemi di sicurezza), ma se c'è qualche ragione per cui non può essere utilizzata, posso fornire una prova di codice concettuale anche per il secondo caso. (Ho verificato che entrambe le opzioni funzionano su Ubuntu 12.04.2 LTS con un kernel x86-64 generico 3.8.0-27, usando il file system ext4.)

Spero che questo aiuti.

+0

L'opzione 1, l'impostazione della radice di setuid sulla libreria di precaricamento mi ha aiutato nel precaricamento. Tuttavia, quando imposto la programmabilità delle funzionalità che ha un paio di modifiche ai limiti dopo 'exec',' prctl (PR_SET_KEEPCAPS, 1L) 'non ha mantenuto le capacità. L'esecuzione del mio programma di test è stata eseguita con 'strace/gdb./Executable' non è riuscito a conservare le funzionalità e ha avuto esito positivo quando è stato eseguito come'./Executable'. Questo perché ha un cambiamento di confine dalla shell a 'strace/gdb' seguito da'./Executable'. Vorrei sapere cosa sta causando le capacità di non mantenere il cambiamento di confine. –

+0

@SunEric: 'prctl (PR_SET_KEEPCAPS, 1L)' è efficace solo per il prossimo 'exec *()', poiché il 'PR_SET_KEEPCAPS' è sempre resettato a 0 dopo una chiamata' exec *() '. Questo è menzionato nella manpage di 'man 2 prctl'. –

4

Sì, è per motivi di sicurezza (vedere man sudo).

Si dovrà lavorare intorno ad esso aprendo esplicitamente la libreria dal codice all'inizio del main() usando dlopen (o avvolgendo principale o simili).

+0

Correggetemi se ho torto a capire di 'ld_preload': la libreria di oggetti condivisa è precaricata molto prima delle funzioni di libreria standard. Se questo è vero, la libreria richiesta è precaricata seguita da 'libc.so'; Come hai suggerito di aprire una libreria usando 'dlopen' nell'applicazione' main() ', i simboli non sono già risolti dalla libreria precaricata o dalla libc quando viene aperto' dlopen'? –