2010-12-11 9 views
5

Vorrei implementare una sandbox tramite ptrace() un processo che inizio e tutti i suoi figli creerebbero (compresi i nipoti ecc.). Il processo genitore ptrace(), ovvero il supervisore. sarebbe un semplice programma C o Python e concettualmente limiterebbe l'accesso al file system (in base al nome del percorso e alla direzione di accesso (lettura o scrittura) e al socket (ad esempio non consentendo la creazione di socket)Come può Linux ptrace essere pericoloso o contenere una condizione di competizione?

Cosa dovrei prestare attenzione in modo che il processo ptrace() d ei suoi figli (in modo ricorsivo) non siano in grado di bypassare la sandbox? C'è qualcosa di speciale che il supervisore dovrebbe fare al tempo fork() per evitare condizioni di gara? È possibile leggere gli argomenti del nome file di es. rename() ? dal processo figlio, senza una condizione di competizione

Ecco quello che ho già programmato di fare:

  • PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE da evitare (alcuni) coditions gara quando fork() ing
  • respingere tutte le chiamate di sistema di default, e comporre una whitelist di sistema ha permesso chiama
  • assicurarsi che le *at() varianti chiamate di sistema (come ad esempio openat) sono correttamente protected

A che altro dovrei prestare attenzione?

+0

Quindi in pratica si sta tentando di replicare il backend basato su ptrace di Systrace (http://www.systrace.org/)? – thkala

+0

Sì, il mio supervisore avrebbe funzionato in modo simile a systrace. Sfortunatamente, systrace sembra non essere mantenuto, non si compila in modo pulito, e sembra anche essere bacato (impiccato indefinitamente quando ho usato GCC sandbox con GNU come). – pts

risposta

11

Il problema principale è che molti argomenti di syscall, come i nomi di file, vengono passati al kernel come puntatori userspace. Qualsiasi attività consentita per l'esecuzione simultanea e con accesso in scrittura alla memoria a cui punta il puntatore può effettivamente modificare questi argomenti dopo essere stati controllati dal supervisore e prima che il kernel agisca su di essi. Nel momento in cui il kernel segue il puntatore, i contenuti puntati potrebbero essere stati deliberatamente modificati da un'altra attività programmabile (processo o thread) con accesso a quella memoria. Per esempio:

Thread 1       Supervisor    Thread 2 
----------------------------------------------------------------------------------------------------- 
strcpy(filename, "/dev/null"); 
open(filename, O_RDONLY); 
            Check filename - OK 
                  strcpy(filename, "/home/user/.ssh/id_rsa"); 
(in kernel) opens "/home/user/.ssh/id_rsa" 

Un modo per fermare questo è quello di non consentire chiamare clone() con la bandiera CLONE_VM, e in aggiunta prevenire qualsiasi creazione di scrivibile MAP_SHARED mappature di memoria (o almeno tenere traccia di loro in modo tale che si nega qualsiasi syscall che cerca di riferire direttamente i dati da tale mappatura). È anche possibile copiare qualsiasi argomento in un buffer di rimbalzo non condiviso prima di consentire a syscall di procedere. Ciò impedirà in modo efficace l'esecuzione di qualsiasi applicazione in thread nella sandbox.

L'alternativa è a SIGSTOP ogni altro processo nel gruppo tracciato attorno a ogni syscall potenzialmente pericoloso, attendere che si fermino effettivamente, quindi consentire a syscall di procedere. Dopo che è stato restituito, è stato effettuato il SIGCONT (a meno che non fossero già stati arrestati). Inutile dire che questo potrebbe avere un impatto significativo sulle prestazioni.

(Esistono anche problemi analoghi con gli argomenti syscall passati nello stack e con tabelle di file aperte condivise).

+0

Accettato, molto perspicace, grazie! – pts

+0

perché la soluzione di copia impedisce le applicazioni filettate? l'applicazione non si aspetta che la chiamata di sistema sia "un po '" atomica, comunque? – keppla

+1

@keppla: perché non è possibile creare un'area di memoria non condivisa in un'applicazione con thread, poiché tutti i thread condividono la stessa VM. La soluzione da copia a non condivisa è per gli argomenti di syscall nelle regioni di memoria condivise con altri processi. – caf

3

Non passa solo le notifiche dopo il fatto? Non penso che tu abbia la possibilità di fermare realmente il syscall, ma solo di ucciderlo il più velocemente possibile una volta che vedi qualcosa di "cattivo".

Sembra che tu stia cercando qualcosa come SELinux o AppArmor, dove puoi garantire che nemmeno una chiamata illegale arriva.

+0

Su Linux, c'è PTRACE_SYSEMU, che può fermare syscall. Grazie anche per aver menzionato le alternative. Tuttavia, non riesco a capire perché SELinux o AppArmor sarebbero più sicuri, a patto che non ci siano bug e condizioni di gara. È una supposizione ragionevole? – pts

+1

Un modo comune di disabilitare un syscall è cambiarlo in getpid (2). – maat

Problemi correlati