[SELinux] La solution pour se protéger d'une 0day

Page content

Avant de commencer, j'aimerais remercier IoT.bzh de m'avoir donné l'opportunité d'écrire cet article.



Prérequis

Vous pouvez lire l’article sur la mise en place d’un environnement SELinux avant cet article.

Dans cet article, toutes les commandes sont lancées sur un serveur fedora 31.

Vous avez besoin d’installer le paquet suivant pour la suite :

$ dnf install attr


Qu’est ce que SELinux ?

Mandatory Access Control

SELinux est un mandatory access control, qui permet d’améliorer la sécurité d’un système. La particularité de celui-ci est que la politique de sécurité est gérée au niveau du système et ne laisse pas à l’utilisateur la gestion des droits sur ses fichiers. Ils utilisent l’infrastructure Linux Security Modules (LSM) du noyau Linux.

Il est important de retenir que les mandatory access control sont appelés après plusieurs étapes. (https://www.linux.com/tutorials/overview-linux-kernel-security-features/). Si une erreur arrive avant, il est donc normal de ne pas avoir d’entrée dans les journaux.

Les journaux SELinux sont disponibles dans le fichier suivant :

$ tail -f /var/log/audit/audit.log

Voici un schéma pour résumer :

label

Politique de sécurité

La politique de sécurité va permettre d’autoriser des actions. Par défaut tout est interdit.

Quand une demande d’autorisation a lieu, SELinux vient d’abord consulter son cache (Access Vector Cache). S’il arrive a trouver une règle qui correspond à la demande, il l’autorise ou non. Dans le cas contraire, SELinux vient regarder directement dans sa polique et cherche et autorise ou non la demande. Cependant, il vient cette fois stocker la règle dans ce cache pour que les prochaines demandes similaires soient plus efficaces.

On va s’intéresser à la politique de sécurité targeted qui est stockée dans ce fichier :

$ file /etc/selinux/targeted/policy/policy.32
/etc/selinux/targeted/policy/policy.32: SELinux policy v32 8 symbols 9 ocons

32 représente la version actuelle de la politique.

La politique de fedora 31 est accessible à cette adresse : https://github.com/fedora-selinux/selinux-policy sur la branche f31.



Comment ça fonctionne ?

Labels

SELinux utilise des labels. Chaque ressource (fichier, socket, processus, port) en a un.

Un label se décompose en quatre parties :

label

Le nommage de ces parties n’est pas obligatoire mais par convention on utilise à la fin :

  • _u pour les utilisateurs SELinux

  • _r pour les rôles SELinux

  • _t pour les types SELinux

Fichier

Lire le label d’un fichier

Pour un fichier, le label est stocké dans les attributs étendus :

$ getfattr -d -m "selinux" /etc/passwd
# file: etc/passwd
security.selinux="system_u:object_r:passwd_file_t:s0"

On peut aussi voir ce contexte avec la commande ls.

$ ls -Z /etc/passwd
system_u:object_r:passwd_file_t:s0 /etc/passwd
Changer le label d’un fichier

Vous aurez l’occasion de modifier le label d’un fichier pendant le développement d’une politique. Il existe plusieurs moyens pour le faire.

Si un fichier n’a pas le bon label au regard de la politique, on peut le restaurer avec la commande restorecon :

# Relabel type of a file
$ restorecon file_to_relabel
# Relabel type recursively
$ restorecon -R directory_to_relabel
# Relabel user, role, type
$ restorecon -F file_to_relabel

Si vous voulez modifier la politique pour ensuite appliquer restorecon, il existe deux possibilités :

  1. Utiliser semanage pour venir modifier la politique directement :
# Change context for a file
$ semanage fcontext -a -t httpd_sys_content_t /var/apache_app/index.html
$ restorecon /var/apache_app/index.html
# Change context for a directory (regex)
$ semanage fcontext -a -t httpd_sys_content_t "/var/apache_app(/.*)?*"
$ restorecon -R /var/apache_app
  1. Créer une politique (Créer une politique SELinux).

Vous pouvez aussi relabéliser un fichier temporairement (dans le cas d’une relabélisation, les modifications seront perdues) avec la commande chcon :

 $ chcon -t httpd_sys_content_t /var/apache_app/index.html

Processus

Lire le label d’un processus

Le label d’un processus est stocké dans un fichier :

# $$ pour le processus actuel
$ cat /proc/$$/attr/current
guest_u:guest_r:guest_t:s0

Il y a d’autres informations intéressantes dans le dossier attr mais nous n’en parleront pas maintenant.

Vous pouvez aussi utiliser la commande id pour récupérer le label de votre session :

$ id -Z
guest_u:guest_r:guest_t:s0

Et vous pouvez récupérer le contexte d’un processus avec la commande ps :

$ ps -eZ  | grep dockerd
system_u:system_r:container_runtime_t:s0 896 ? 00:24:46 dockerd
Changer le label d’un processus

Pour changer le label d’un processus, il faut réaliser une transition de domaine. On verra un peu plus tard comment réaliser cette transition car ce n’est pas la partie la plus simple !


Pour le reste de l’article, on s’intéressera uniquement à la partie TYPE des labels.

Pour plus d’informations sur les parties utilisateur et rôles, je vous invite à lire l'article consacré.

La partie type est aussi appelée contexte ou domaine.

Règles

Allow

Règles standards

Comme on l’a dit plus tôt, les règles SELinux sont stockées dans la politique de sécurité et fonctionnent comme une liste blanche.

Il existe deux langages pour écrire des règles SELinux :

  • le langage CIL (Commin Intermediate Language)
  • le langage de base (kernel policy language)

Voici des détails et les motivations du nouveau langage CIL : https://github.com/SELinuxProject/cil/wiki

Les règles classiques SELinux se définissent de la manière suivante :

allow server_t server_t:tcp_socket {listen read}
#        1             2                 3
  1. Le domaine qui demande l’autorisation

  2. L’objet qui est manipulé

  • server_t est le contexte de l’objet
  • tcp_socket est la classe de l’objet (fichier, socket, …)
  1. Les autorisations
Macros and defines

Le langage de base supporte aussi la gestion de macros grâce à m4. Cela permet de réduire la taille des instructions.

Par exemple, pour créer une “stream socket”, on a besoin de définir les règles suivantes :

allow server_t server_t:tcp_socket {ioctl read getattr lock write setattr append bind connect getopt setopt shutdown create listen accept}

Il est facile de faire une erreur ou d’oublier quelque chose. On va utiliser les define pour nous simplifier le travail.

Pour la suite de l’article, on utilise des commandes évoquées dans l’article “Créer un environnement SELinux”.

$ sefinddef "stream.*socket"
...
# Permissions pour créer et utiliser des stream sockets
define(`create_stream_socket_perms', `{ create_socket_perms listen accept }')
...

On peut voir que ce define contient aussi un autre define create_socket_perm :

$ seshowdef "create_socket_perm"
define(`create_socket_perms', `{ create rw_socket_perms }')

Et un autre rw_socket_perms :

$ seshowdef "rw_socket_perms"
define(`rw_socket_perms', `{ ioctl read getattr lock write setattr append bind connect getopt setopt shutdown }')

On peut se rendre compte que toutes les autorisations évoquées précédemment se trouvent dans create_socket_perms. On peut définir une règle équivalente comme celle ci :

allow server_t server_t:tcp_socket create_stream_socket_perms;

Dontaudit

Si vous utilisez dontaudit plutôt que allow, l’opération sera refusée mais elle ne sera pas journalisée.

Il est donc possible d’avoir une règle qui bloque une commande et qui n’est pas auditée.

Pour forcer l’affichage de tous les logs, on peut utiliser :

$ semodule -DB

Et pour revenir dans l’état initial :

$ semodule -B

Auditallow

auditallow a le même effet que allow, mais la règle autorisée sera journalisée.

Neverallow

Nous avons dit plus tôt que tout était interdit par défaut sur SELinux. C’est vrai, mais neverallow est une règle préventive. Même si une nouvelle règle autorise une action, elle sera toujours bloquée.

Booléens

SELinux définit des booléens pour changer le comportement d’une politique sans la recompiler.

Pour lister les booléens, vous pouvez utiliser la commande suivante :

$ semanage boolean -l
SELinux boolean                State  Default Description
abrt_anon_write                (off  ,  off)  Allow abrt to anon write
abrt_handle_event              (off  ,  off)  Allow abrt to handle event
abrt_upload_watch_anon_write   (on   ,   on)  Allow abrt to upload watch anon write
antivirus_can_scan_system      (off  ,  off)  Allow antivirus to can scan system
...

Dans cet extrait, on a plusieurs informations :

  • abrt_anon_write : Le nom du booléen
  • (off , off) : L’état actuel du booléen est off et son état par défaut est off
  • “Allow…write” : La description du booléen

Intéragir avec les booléens

On peut récupérer l’état d’un booléen avec la commande getsebool :

$ getsebool abrt_anon_write
abrt_anon_write --> off
$ getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
abrt_upload_watch_anon_write --> on
antivirus_can_scan_system --> off

Et changer son état avec setsebool :

$ setsebool abrt_anon_write on
$ setsebool abrt_anon_write off

Un booléen est représenté dans la politique avec les lignes suivantes :

## <desc>
## <p>
## Allow ABRT to modify public files
## used for public file transfer services.
## </p>
## </desc>
gen_tunable(abrt_anon_write, false)

## ==> gen_tunable will generate boolean with default 
## value at false in this case

...

tunable_policy(`abrt_anon_write',`
	miscfiles_manage_public_files(abrt_t)
')

Transition de domaine

Les transitions de domaine sont un point très important dans SELinux. Elles permettent de changer le contexte d’un processus quand on exécute une application.

Imaginons que nous lancions une application. On ne veut pas qu’elle s’exécute dans le même contexte que celui de l’utilisateur qui l’a lancée. La transition de domaine va donc prendre place au moment de l’exécution et va permettre aux deux contextes de rester séparés.

On peut schématiser ce comportement avec l’exemple suivant :

domain

Pour faire une transition de domaine, il y a trois prérequis :

  • Le processus du domaine qui demande la transition doit en avoir le droit (proccesus)
  • Le sujet a les droits d’execution sur l’objet (fichier)
  • Le contexte du script de lancement est un point d’entrée (fichier)

Sources

Vermeulen, S. (2014). SELinux Cookbook : over 70 hands-on recipes to develop fully functional policies to confine yours applications and users using SElinux. Packt Publishing.

Vermeulen, S. (2016). SELinux System Administration. Packt Publishing.

SELinux/Users and logins - Gentoo Wiki. (n.d.-b). Retrieved April 7, 2020, from https://wiki.gentoo.org/wiki/SELinux/Users_and_logins

Security-Enhanced Linux Red Hat Enterprise Linux 6. (n.d.). Retrieved April 7, 2020, from https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security-enhanced_linux/index

Tresys. (2006, August 19). SELinux Policy Concepts and Overview [Slides]. Retrieved from http://www.cse.psu.edu/~trj1/cse543-f07/slides/03-PolicyConcepts.pdf

Debian. (n.d.). The Debian Administrator’s Handbook. Retrieved April 7, 2020, from https://debian-handbook.info/browse/stable/