Limiter le nombre de processus créés avec ulimit

I. Présentation

Plusieurs sociétés éditrice de système d’exploitation ont choisi de cacher les spécifications de leur produit. Pourquoi ?

1 - En cachant et en verrouillant les spécifications de leurs systèmes, ils évitent (croient éviter ?) qu’un concurrent se base sur leur code pour l’améliorer et en produire un code meilleur, moins bugué et au final produire un système meilleur.

2 - En mettant une couche opaque entre les utilisateurs et le code système, ce dernier devient selon eux moins sujette aux failles et à leurs découvertes puisque on ne divulgue aucunes informations sur la structure du système, son implémentation, etc …, c’est ce qu’on appelle la sécurité par l’obscurité, qui donne une fausse impression de fiabilité et/ou de sécurité.

Mais voilà, ceci a peut-être donné ses fruits à un moment donné, mais c’est loin d’être le cas de nos jours.

Néanmoins, tous les systèmes heureusement ne sont pas pareils et un d’eux sort tout particulièrement de ce lot et le fait savoir, son nom … Linux !

ULIMIT0

D’ailleurs il suffit de démarrer un système à base d’un noyau Linux (par exemple GNU/Linux Debian Wheezy) pour être convaincu ! Linux ne cache rien et dès le démarrage il montre ce qu’il fait : de l’initialisation du système (chargement de l’initrd, exécution du noyau) en passant par le démarrage des services, tout y est !

ULIMIT1

Le code de Linux a toujours été ouvert, permet facilement de l’étudier, de le modifier et de le redistribuer à souhait. L’ouverture du code du Noyau Linux à permit certes de le soumettre aux attaques et aux vulnérabilité car toute sa structure est visible, mais il a aussi permis de le soumettre à un large audit de code d’une communauté grandissante pour au final mieux le protéger contre ses mêmes vulnérabilités, de le tester sans cesse, encore et encore et ainsi arrivé au final à produire un système bien plus robuste, fiable.

A travers les articles qui vont suivre, nous aurons l’occasion de gratter quelques couche du noyau Linux pour étudier (en surface) le fonctionnement interne de ce noyau, manipuler la libc, et de tester quelque vulnérabilités auxquelles peut être sujet un système à base d’un noyau Linux. Nous verrons comment les éviter avec des commandes d’administration système puis au final nous toucherons un peu au langage C et voir par quels moyens ses mêmes commandes sont implémentées.

Pourquoi ses étapes ? La maîtrise d’un système et son administration passe indéniablement par la compréhension de son fonctionnement interne !

II. Exemple de Fork Bomb

L’une des commandes les plus simples pour faire tomber un noyau Linux se résume à a peine quelques caractères, tapez ceci dans un terminal (à ne pas tester sur un système en production !) :

:(){ :|:& };:

Alors ? Tout est bloqué ? C’est normal ! Vous venez d’exécuter un genre de Déni de Service sur votre propre système d’exploitation. Cette commande se nomme une « Fork Bomb ».

ULIMIT2

III. Explication

Cette commande permet de créer des processus à foison qui se duplique sans aucune limitation ! Ses processus saturent la table des processus tenu par le noyau, bouffe tous les cycles du CPU ainsi que la mémoire du système ! Ce dernier devient instable et très rapidement inutilisable. Seul moyen de guérison, tuer toutes les instances de processus crées, ce qui est possible dans notre cas qu’en redémarrant le système. La connexion avec un autre terminal via les raccourcis claviers Ctrl + Alt + F* est impossible.

IV. Prévention

Pour se prémunir des futures utilisations de ce genre d’attaque, on peut limiter le nombre de processus qui peuvent être créés pour un utilisateur ou une application donnée. A noté que des limites par défauts sont chargées lors de la connexion d’un utilisateur grâce à PAM. Le fichier /etc/security/limits.conf chargé par PAM contient les limites soft (qui peuvent être dépassées mais on sera averti par le noyau) et les limites durs (qui ne peuvent être franchisses sous peine d’une action extrême de la part du noyau) pour chaque utilisateur ou groupe d’utilisateurs.

En administration système, cela se résume à l’appel de la commande ulimit avec l’option –u pour changer le nombre de processus pouvant être créé, exemple :

ulimit –u 10 (permet de limiter à 10 le nombre de processus qui peuvent être créés par un utilisateur)

On peut vérifier que cela a été pris en compte en tapant dans un terminal la commande :

ulimit –u

Ou

ulimit –a (pour voir toutes les limitations)

Nota bene : La limitation des processus pouvant être créé doit être configuré avec attention. On doit limiter le nombre de processus certes mais pas au point de bloquer définitivement le travail d’un utilisateur. Dans notre exemple, la limitation à 10 processus est a utilisé à titre expérimental.

V. Implémentation

En langage C, l’implémentation de la commande ulimit est faite grâce aux appels système getrlimit et setrlimit définies dans sys/time.h et sys/resource.h, ses deux fonctions permettent de récupérer et de configurer les limites de ressource liées à un processus (ou utilisateur, groupe, etc …).

int getrlimit(int ressource, struct rlimit *rlimit)

Le premier paramètre et un entier définissant la ressource dont on veut prendre le contrôle. Dans notre cas (i.e : limitation du nombre de processus créer), ressource vaut RLIMIT_NPROC.

Le deuxième paramètre est un pointeur sur une structure comportant deux champs qui définissent respectivement la limite soft (définie par le noyau et qui peut être franchis) et la limite hard (qui ne peut être franchis).

Exemple d’utilisation :

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(){
struct rlimit limite_nb_proc ;
if(getrlimit(RLIMIT_NPROC,&limite_nb_proc) != 0){
fprintf(stdout,”Impossible de récupérer la limite NPROC \n”);
exit(EXIT_FAILURE) ;
}
fprintf(stdout,”RLIMITE_NPROC soft= %d\n”,limite_nb_proc.rlim_cur) ;
fprintf(stdout,”RLIMITE_NPROC hard= %d\n”,limite_nb_proc.rlim_max) ;
exit(EXIT_SUCCESS)
}

Dans le cas d’une modification, on fait l’inverse, à savoir remplir à la main une structure rlimit avec les valeurs soft et hard des limites voulues :

const Struct rlimit limite_nb_proc ;
limite_nb_proc.rlim_cur = 10 ;
limite_nb_proc.rlim_max = 20 ;

Et on fait appel à la fonction setrlimit pour positionner les valeurs :

setrlimit(RLIMIT_NPROC,&limite_nb_proc) ;

Bien sûr ses fonctions sont très configurables, je vous renvoie donc comme d’habitude au man page.

Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Partager sur Google+ Envoyer par mail

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

 

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.