Quelques bricoles pour renforcer Linux : ASLR, lockdown, PTI (Spectre/Meltdown)...

Linux a lui aussi droit à son confinement ! Nous allons voir quelques paramètres non-exhaustifs pour renforcer sa sécurité.

Quelques bricoles pour renforcer Linux : ASLR, lockdown, PTI (Spectre/Meltdown)...

Avertissement

Je préfère prévenir avant de commencer :

  • Cet article n'a pas pour ambition de se substituer aux documentations officielles, ni de se suffire à lui-même. J'y partage simplement quelques mesures non-exhaustives à compléter et dont leur impact est à mesurer individuellement.
  • Tous les paramètres (ainsi que linux-hardened) sont à tester un par un, idéalement sur une machine d'essai (pourquoi pas une VM) avant de les mettre en place sur vos machines personnelles.
  • Enfin, ces "bricoles" sont ce qu'elles sont, rien de plus : bien qu'elles pourraient augmenter drastiquement la protection de Linux contre d'éventuels exploits, elles ne "réparent" pas ses déficiences.

La messe est dite.

Contexte

Il raconte sa vie, passez si c'est trop long...

Il n'y a pas si longtemps, j'évoquais l'insécurité du modèle Linux desktop en insistant parfois sur la surface d'attaque que représente Linux. Pour résumer (en vrac) : trop de privilèges (laissant place à une guerre philosophique), noyau monolithique, attaques possibles liées à la mémoire, etc.

Récemment, j'ai expliqué comment mettre en place des solutions d'isolation particulières telles qu'avec gVisor. Si vous pensiez que ce n'était réservé qu'aux conteneurs, détrompez-vous : gVisor (sous réserve de maturation du projet) pourrait tout à fait subvenir aux besoins d'applications du quotidien. GrapheneOS vise à utiliser gVisor dans une optique de moyen terme.

Fuchsia, perçu comme le remplaçant d'Android et qui utilisera un microkernel (kind of), utilisera sans doute une couche de compatibilité similaire à gVisor : une émulation de l'API Linux dans l'espace utilisateur. On pourrait peut-être y voir dans gVisor les fondations de ce travail...

Je ne suis personnellement plus vraiment utilisateur de Linux sur desktop, mais j'ai entendu les critiques de certains par rapport à mon premier article (qui a été mis à jour continuellement). Parmi elles, j'ai vu que "je ne proposais rien" : en effet, je suggère implicitement de passer sur un OS avec un vrai modèle de sécurité, mais je laisse la porte ouverte à une amélioration future.

Malgré tout, j'utilise Linux sur mes serveurs (et mon smartphone Android), et je ne vois pas comment je pourrais m'en passer. Donc non, je ne m'en fous pas ; et oui, j'ai quelques "solutions" à proposer dont je ne suis que le médiateur, je n'ai rien inventé ici.

linux-hardened

C'est quoi ?

Pour les plus téméraires d'entre vous, vous pouvez simplement utiliser linux-hardened. C'est un projet qui vise à fournir des patchs relativement légers à Linux et renforcent sa sécurité sans non plus sacrifier toutes les performances. Si vous aviez lu mes articles, ce n'est pas la première fois que je mentionne linux-hardened : en effet, ce projet émane directement de GrapheneOS qui l'utilise.

anthraxx/linux-hardened
Minimal supplement to upstream Kernel Self Protection Project changes. Features already provided by SELinux + Yama and archs other than multiarch arm64 / x86_64 aren't in scope. Only tags have ...

Voyez linux-hardened comme une alternative à grsecurity/PaX.

Ah, vous vous souvenez de grsecurity ? Malheureusement, leurs patchs sont devenus payants.

"Hardened", comment ça ?

Vous allez rire mais il n'y a pas vraiment de page qui recense toutes les améliorations apportées, et c'est bien dommage ; les mainteneurs réfèrent souvent à l'historique des commits.

J'ai fait un petit travail de recensement et voici ce que vous pouvez noter :

  • Beaucoup d'améliorations notables à ASLR (Address Space Layout Randomization), une technique de défense largement employée qui place aléatoirement les adresses mémoires d'un processus pour rendre plus difficile les attaques de type buffer overflow.
  • Restrictions d'accès par les capabilities, renforcement de l'allocation Slab, etc.
  • Compilation avec FORTIFY_SOURCE, qui avec GCC repose sur la stack SSP pour limiter des attaques de buffer overflow (mais pas toutes).
  • Le projet suggère toutefois d'être compilé avec clang plutôt que GCC pour bénéficier de CFI/ShadowCallStack, dont seul Google tire à ce jour profit pour les kernels des Google Pixel.
  • Des paramètres par défauts considérés comme robustes et sûrs, dont la plupart sera vue ultérieurement.

Au fond, ça reste un patch assez minimaliste qui ne doit pas être utilisé tout seul. Par exemple, le projet vous suggère de mettre en place vous-même votre MAC (mandatory access control) tel que AppArmor ou SELinux.

Stable ou LTS (long term support) ?

linux-hardened propose des patchs pour les versions stables et LTS de Linux.

  • Les versions stables sont plus souvent mises à jour en termes de fonctionnalité (parfois de sécurité) : elles peuvent introduire, outre la surface d'attaque supplémentaire, des bugs et donc des failles potentielles.
  • Les LTS sont moins souvent mises à jour, mais vous avez des fonctionnalités stables et souvent éprouvées.

C'est une question de compromis, mais j'aurais tendance à suggérer les LTS sur des machines qui doivent rester "stables" justement, et les versions "stable" pour tout le reste. Dans tous les cas, ne négligez pas les mises à jour de votre kernel (surtout sur serveur, je vous vois avec vos concours d'uptime).

Bonus : lien intéressant sur le sujet.

Installation

AVERTISSEMENT : linux-hardened peut "casser" des logiciels que vous utilisez donc il faut absolument tester en amont, et prévoir un downgrade en cas de besoin.

Si vous utilisez Arch Linux, c'est votre jour de chance : linux-hardened y est proposé comme paquet. Autrement, on va devoir compiler.

Pour ce tutoriel très bref, j'utilise Debian Buster à jour configuré pour recevoir des paquets des backports. Ces derniers vous permettent de bénéficier de certains paquets à jour et de la même qualité que les paquets standards, donc je vous suggère très fortement de l'utiliser.

Commençons par installer les prérequis à la compilation de Linux (avec permissions administrateur) :

# apt install build-essential xz-utils libncurses-dev bison flex libssl-dev libelf-dev

Maintenant petit point particulier, la version de pahole sur Debian Buster est trop vieille pour compiler des versions récentes. Donc on va l'installer depuis les backports :

# apt install -t buster-backports dwarves

Désormais les permissions administrateur ne seront plus nécessaires excepté pour l'installation proprement dite. Maintenant vous pouvez créer un dossier de travail, y télécharger les sources de la version souhaitée et l'extraire tout en vérifiant son authenticité :

$ VERSION_LINUX=5.11.1 # à remplacer
$ mkdir linux-hardened && cd linux-hardened
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${VERSION_LINUX}.tar.xz
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${VERSION_LINUX}.tar.sign
$ xz -d -v linux-${VERSION_LINUX}.tar.xz
$ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org
$ gpg2 --verify linux-${VERSION_LINUX}.tar.sign
$ tar xvf linux-${VERSION_LINUX}.tar

VERSION_LINUX est à changer selon vos besoins et la disponibilité des patchs de linux-hardened (cf. les releases sur Github). Tant qu'à faire, téléchargeons le patch correspondant et appliquons-le :

$ wget https://github.com/anthraxx/linux-hardened/releases/download/${VERSION_LINUX}-hardened1/linux-hardened-${VERSION_LINUX}-hardened1.patch
$ cd linux-${VERSION_LINUX}
$ patch -p1 < ../linux-hardened-${VERSION_LINUX}-hardened1.patch

Enfin, il ne reste que des étapes classiques pour entamer la compilation proprement dite. Vous pouvez partir d'une .config pré-existante (facultatif) :

$ cp -v /boot/config-$(uname -r) .config

Puis lancer menuconfig, apporter vos modifications si nécessaires, enregistrer puis quitter :

$ make menuconfig

Si vous avez utilisé un .config de kernel Debian, pensez à commenter deux lignes dans ce fichier sans quoi la compilation ne commencera pas :

CONFIG_MODULE_SIG_KEY
CONFIG_SYSTEM_TRUSTED_KEYS

Au lieu de fournir un certificat qui n'existera pas à moins de l'importer manuellement, vous allez en générer un vous-même.

La compilation proprement dite peut être lancée :

$ make -j x

"x" à remplacer par le nombrer de cœurs CPU que vous souhaitez dédier à la compilation.

Le temps de se faire un thé vert (ou deux), la compilation devrait se finir sans obstacles et vous devez passer en administrateur pour installer le kernel :

# make modules_install
# make install

Normalement GRUB (ou apparenté) devrait se configurer tout seul, mais si ce n'est pas le cas, passez un coup de update-grub. Enfin, on redémarre et on regarde si le kernel est bien installé :

$ uname -a
Linux xxxxx 5.11.11-hardened1 #1

Bien, c'est fait !

Linux vanilla : quoi faire ?

Personnellement, je n'utilise pas linux-hardened (sauf sur GrapheneOS du coup) même si je trouve le projet très intéressant. Mais c'est un peu overkill pour moi d'autant plus que je fais très attention à la surface que j'expose à mes applications.

Je suis aussi un gros flemmard qui n'a pas envie de compiler à chaque mise à jour et je préfère faire confiance à l'équipe Debian pour cela.

Un noyau récent

Tout d'abord je vous conseille d'utiliser une version LTS/stable récente, par exemple si vous utilisez Debian Buster, passez par les backports :

apt install -t buster-backports linux-image-amd64

Je vous réfère au paragraphe plus haut concernant le compromis stable/LTS.

Les kernel proposés par les distributions sont généralement de bonne qualité et disposent d'une maintenance dans l'ordre du possible. Seulement le système de CVE n'est pas infaillible, y compris pour Linux, ce qui peut brouiller le travail de backports et c'est ce que je tentais d'expliquer dans un article précédent. C'est pour cette raison que je privilégie des kernel récents.

Compiler ou pas ?

Vous pouvez tout à fait reprendre les instructions précédentes, exclure l'étape du patch et compiler votre propre kernel. Cela a plusieurs avantages :

  • Vous pouvez réduire la surface d'attaque en configurant un kernel minimal avec seulement ce dont vous avez besoin.
  • Certains types d'attaques exploitent des emplacements connus de pointeurs : en compilant votre propre kernel, vous aurez des pointeurs uniques ce qui rend ces attaques reposant sur des emplacements définis plus difficiles.
  • Ne pas dépendre d'un mainteneur pour mettre à jour son kernel (il faut), mais à voir si l'effort vaut la peine pour vous.

Si vous décidez de compiler, cette page vous indiquera des options de configuration intéressantes à mettre en place.

Linux Security Modules & MAC

LSM (Linux Security Modules) est un framework de Linux qui permet par exemple aux MAC (AppArmor, SELinux, TOMOYO) de fonctionner. Ce sont des outils puissants qui demandent d'être particulièrement intégrés au kernel, et sont également très complexes, si bien que ce ne sera pas dans l'intérêt de ce billet d'aller dans le détail.

Un MAC permet de restreindre un programme à ses accès spécifiques (chemins, ports, etc.) permettant ainsi d'éviter une catastrophe en cas de compromission dudit programme.

Sachez que SELinux permet un contrôle bien plus précis, mais il est aussi bien plus complexe à maîtriser. AppArmor est plus simple, mais moins précis. Pour le moment, je conseille d'utiliser une distribution qui s'intègre avec l'un des deux : Debian Buster utilise AppArmor par exemple.

Je vous réfère en conséquence à la documentation de votre distribution.

Pour savoir quels LSM sont utilisés :

# cat /sys/kernel/security/lsm
lockdown,capability,yama,apparmor,tomoyo

Linux se fait aussi confiner (lockdown mode)

Depuis sa version 5.4, Linux embarque une nouvelle fonctionnalité : le mode lockdown. Proposé par l'ingénieur de Google Matthew Garett, ce mode a mis un bon bout de temps avant d'être intégré upstream. Et pour cause, il s'attaque à un problème philosophique de taille : les pleins privilèges.

Traditionnellement, la frontière entre le kernel et l'espace utilisateur est mince. Beaucoup d'informations sensibles s'échangent de l'un à l'autre, et en particulier, l'espace utilisateur a un un pouvoir de modification puissant sur le kernel notamment par l'utilisateur root.

Mais est-ce que root doit vraiment avoir les "pleins pouvoirs", même sur Linux proprement dit ?

Et c'est Linus Torvalds lui-même qui était très critique de l'idée de verrouiller des aspects du kernel, avant de céder après des années. Ce mode a été plus ou moins implémenté auparavant par certaines distributions.

Un des facteurs qui a poussé à l'inclusion de ce mode est l'avènement de l'UEFI Secure Boot. En effet, son modèle de sécurité n'a que très peu de sens si l'userspace peut à ce point altérer en profondeur le système et, dans le cas d'un malware, gagner facilement la persistance au démarrage.

Entre autres, le mode lockdown agit de plusieurs façons en désactivant des fonctionnalités qui peuvent altérer le noyau :

  • Intransigeance sur la signature des modules utilisés (potentiel conflit avec dkms, je vous préviens d'avance)
  • Accès restreint à /dev/mem, /dev/kmem et /dev/port
  • Verrouillage du DMA (Direct Memory Addressing) par l'insertion d'appareils (PCI par exemple), etc.

Tout ce qu'il faut retenir, c'est que le lockdown empêche généralement à l'userspace (même root) d'altérer le fonctionnement du kernel.

lockdown possède deux modes :

  • integrity : c'est le niveau d'implémentation de base qui restreint l'accès de l'userspace au kernel (bye Kexec, BPF)
  • confidentiality : c'est un superset du mode précédent qui empêche même root d'accéder à des informations sensibles (comme des clés EVM)

Le lockdown peut être activé dans un paramètre de démarrage (par exemple GRUB_CMDLINE_LINUX_DEFAULT dans /etc/default/grub):

lockdown=integrity

A l'issue d'un redémarrage, on peut vérifier que le lockdown est actif :

# cat /sys/kernel/security/lockdown
none [integrity] confidentiality

Attention, donc : le mode lockdown empêche de charger des modules qui ne sont pas signés, certaines fonctionnalités de fonctionner (Kexec, BPF), mais apparemment les capacités d'hibernation également. C'est une option intéressante à activer au cas par cas.

Autres paramètres de démarrage

Ces paramètres peuvent être appliqués de la même façon que le lockdown. En voici quelques uns que j'ai sélectionné, et qui ne devraient pas énormément vous impacter.

Cette liste est inspirée par celle gracieusement fournie par madaidan, chercheur en sécurité et développeur chez Whonix. Il fournit davantage de paramètres, mais j'en ai sélectionné quelques uns seulement qui n'impactent pas autant les performances.

slab_nomerge

Allow slab_nomerge to be set at build time (openwall.com)

vsyscall=none

Obsolètes et remplacés par vDSO

slub_debug=FZ

Permet de prévenir des corruptions liées à slab (des pages contiguës en mémoire utilisés dans des opérations de cache).

init_on_alloc=1 init_on_free=1

Permet de prévenir des fuites d'information sensibles, et des vulnérabilités Use After Free (qui permettent des executions de code arbitraire via des pointeurs qui ne pointent pas vers un objet propre).

page_alloc.shuffle=1

Randomise l'allocation de pages en mémoire, peut aussi bénéficier en performances.

debugfs=off

Désactive debugfs qui expose des informations sensibles.

Page-Table Isolation

pti=on

PTI (anciennement KAISER) est l'acronyme de Page-Table Isolation, une stratégie de mitigation de la fameuse vulnérabilité Meltdown, et qui plus généralement protège des bypass de l'ASLR mentionné plus haut. Cette mitigation a cependant un coût théorique en performance (variable selon l'architecture CPU et la tâche, donner un % n'a pas vraiment de sens).

Je vous conseille par ailleurs (chez Debian en tous cas) l'excellent outil spectre-meltdown-checker qui permet d'avoir une vue globale de toutes les vulnérabilités liées à Spectre/Meltdown et vos mitigations mises en place.

Exemple de PTI en action

Une fois ces paramètres ajoutés, vous pouvez redémarrer et tester par exemple avec cet utilitaire (qui vous permettra de tester lockdown par la même occasion car il vous hurlera dessus sur certains points...).

Fin et documentation

Vous pouvez creuser le sujet davantage à ces adresses :

Je vous réfère également à mes précédents articles qui distillent d'autres informations, je voulais parfois éviter la redite. Celui-ci devait être un petit article sans prétention à la base, mais j'ai encore dérapé sur la longueur à certains endroits, veuillez m'excuser de mon manque de concision.

Stay safe!