2010-12-11 4 views
5

Je voudrais implémenter un sandbox par ptrace() dans un processus que je démarre et que tous ses enfants créeraient (y compris les petits-enfants etc.). Le processus parent ptrace(), c'est-à-dire le superviseur. conceptuellement, cela limiterait l'accès au système de fichiers (en fonction du nom du chemin et de la direction d'accès (lecture ou écriture) et de l'accès au socket (par exemple, interdire la création de socket)Comment Linux Ptrace peut-il être dangereux ou contenir une condition de concurrence?

Que dois-je faire attention pour que le processus ptrace() d et ses enfants (récursivement) ne sera pas en mesure de contourner le bac à sable? Y at-il quelque chose de spécial le superviseur devrait faire à fork() temps pour éviter les conditions de course? Est-il possible de lire les arguments du nom de rename() ? du processus enfant sans condition de course

Voici ce que je l'ai déjà prévu de faire:

  • PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE à éviter (certains) coditions de course quand fork() ing
  • interdire tous les appels système par défaut, et cadrez une liste blanche du système a permis appels
  • assurez-vous que les variantes d'appel du système *at() (tels que openat) sont correctement protégé

À quoi d'autre devrais-je faire attention?

+0

Donc, fondamentalement, vous essayez de répliquer le backend basé sur ptrace de Systrace (http://www.systrace.org/)? – thkala

+0

Oui, mon superviseur fonctionnerait de la même manière que systrace. Malheureusement, systrace semble être non maintenu, il ne compile pas proprement, et il semble aussi être bogué (il a pendu indéfiniment quand j'ai mis en sandbox GCC fonctionnant sous GNU as). – pts

Répondre

11

Le problème majeur est que de nombreux arguments syscall, comme les noms de fichiers, sont transmis au noyau en tant que pointeurs d'espace utilisateur. Toute tâche autorisée à s'exécuter simultanément et disposant d'un accès en écriture à la mémoire vers laquelle pointe le pointeur peut effectivement modifier ces arguments après avoir été inspectés par votre superviseur et avant que le noyau n'agisse dessus. Au moment où le noyau suit le pointeur, le contenu pointé peut avoir été délibérément modifié par une autre tâche ordonnancée (processus ou fil) avec accès à cette mémoire. Par exemple:

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" 

Une façon d'arrêter est d'interdire d'appeler clone() avec le drapeau CLONE_VM, et en plus d'éviter toute création d'applications de WCM MAP_SHARED (ou au moins garder une trace de ce que vous niez toute syscall qui essaie de référencer directement les données d'un tel mappage). Vous pouvez également copier un argument de ce type dans un tampon de rebond non partagé avant de permettre au syscall de continuer. Cela empêchera efficacement toute application threadée de s'exécuter dans le bac à sable.

L'alternative est de SIGSTOP tous les autres processus dans le groupe tracée autour de chaque appel système potentiellement dangereux, attendez qu'ils s'arrêtent réellement, puis laissez le syscall se poursuivre. Après le retour, vous les avez ensuite (sauf s'ils étaient déjà arrêtés). Inutile de dire que cela peut avoir un impact significatif sur les performances.

(Il existe également des problèmes analogues avec les arguments syscall transmis sur la pile et avec les tables de fichiers ouvertes partagées).

+0

Accepté, très perspicace, merci! – pts

+0

Pourquoi la solution de copie empêche-t-elle les applications threadées? Est-ce que l'application ne s'attend pas à ce que l'appel système soit "un peu" atomique, de toute façon? – keppla

+1

@keppla: Parce que vous ne pouvez pas créer une région de mémoire non partagée dans une application threadée, puisque tous les threads partagent la même machine virtuelle. La solution de contournement copie-non-partagée concerne le cas des arguments syscall dans les régions de mémoire partagées avec d'autres processus. – caf

3

Est-ce que ptrace ne reçoit que des notifications après le fait? Je ne pense pas que vous ayez une chance d'arrêter le syscall, mais de le tuer aussi vite que vous le pouvez une fois que vous voyez quelque chose de "maléfique".

Il semble que vous soyez plus à la recherche de quelque chose comme SELinux ou AppArmor, où vous pouvez garantir que même un appel illégal ne passe pas.

+0

Sous Linux, il y a PTRACE_SYSEMU, qui peut arrêter le syscall. Merci également d'avoir mentionné des alternatives. Cependant, je ne vois pas pourquoi SELinux ou AppArmor seraient plus sûrs - à condition qu'il n'y ait pas de bugs et de conditions de course. Est-ce une hypothèse raisonnable? – pts

+1

Une manière courante de désactiver un syscall est de le changer en getpid (2). – maat

Questions connexes