# Sécurisation d'un serveur Linux sous debian :robot_face: Romain CARRE :angel: Benjamin INGLEDEW :black_joker: Rahiti PETIT ## Présentation Ce document à pour but de présenter les mesures de sécurité par ordre de priorité donné par l'ANSSI. Ce document démontre aussi comment mettre en place ses mesures. ## Environnement Toutes les mesures on été misent en place sur une machine virtuelle Debian 11.5.0 ## Analyses initales Suite à l'installation du serveur, la première étape à réaliser est un lancement de scan `debsecan` et `lynis` afin de vérifier le niveau de sécurité initial du serveur. *Analyse `debsecan`* ```sh debsecan --suite $(lsb_release --codename --short) --only-fixed --format detail # Returns nothing debsecan --suite $(lsb_release --codename --short) --format detail # CVE-2022-3037 # Use After Free in GitHub repository vim/vim prior to 9.0.0322. ... # installed: xxd 2:8.2.2434-3+deb11u1 # (built from vim 2:8.2.2434-3+deb11u1) # [...] ``` *Analyse `lynis`* ```sh /usr/sbin/lynis audit system | ansi2html -la > report.html # Hardening index : 61 [############ ] ``` Le scan de Lynis nous à mis une note de 61, nous allons mettre en place des mesures de durcissement afin d'augmenter ce score. ## Début du hardening Le hardening est effectué en se basant sur nos expériences personnelles et principalement sur différents référentiels de l'ANSSI: * https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf * https://www.ssi.gouv.fr/uploads/2014/01/NT_OpenSSH.pdf * https://www.ssi.gouv.fr/uploads/2013/05/anssi-guide-recommandations_mise_en_oeuvre_site_web_maitriser_standards_securite_cote_navigateur-v2.0.pdf ### Repartitionnement Niveau de durcissement : `I` Cette section consiste à modifier les options des différents points de montage (voir page 16 du [référentiel](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf)) de sorte à restreindre certains droits * Cacher les processus des autres utilisateurs ([tutorial](https://www.cyberciti.biz/faq/linux-hide-processes-from-other-users/)) ```bash echo 'proc /proc proc defaults,nosuid,nodev,noexec,relatime,hidepid=2 0 0' >> /etc/fstab ``` * Retirer les SUID et les droits d'exécution des programmes dans `/dev/shm` ([tutorial](https://unix.stackexchange.com/questions/670362/mounting-dev-shm-with-noexec)) ```bash echo 'tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0' >> /etc/fstab ``` ### Modification de `UMASK` et des droits des répertoires `/home` Niveau de durcissement : `R` La modification de `UMASK` et des droits des répertoires `/home` est important pour le durcissement du serveur. En effet, cela empêche que différents utilisateurs voient les documents des autres utilisateurs. Les dossiers présents dans `/home` doivent être lisible seulement par les utilisateurs auxquels ils appartiennent. Cette mesure empêche d'avoir la main sur des fichiers potentiellement sensibles et à caractère personnel. ```bash chmod 770 /home/* sed -i 's/^UMASK.*/UMASK 0077/g' /etc/login.defs ``` Ici, nous commençons par changer les droits des répertoires déjà existants avant de modifier le fichier de configuration utilisé lors de la création de nouveaux utilisateurs. ### Modification de la configuration SSH Niveau de durcissement : `I` Les modifications au niveau de la configuration SSH sont mises en place afin d'éviter une connexion **root** et renforce le système d'authentification. En mettant en place les mesures suivantes, nous rajoutons une couche de sécurité supplémentaire au service SSH Les méthodes utilisées * Désactiver la connexion de **root** ([tutorial](https://mediatemple.net/community/products/dv/204643810/how-do-i-disable-ssh-login-for-the-root-user)) ```bash # /etc/ssh/sshd_config PermitRootLogin no ``` * Obliger l'authentification par clé **ET** mot de passe ([tutorial : method 2](https://askubuntu.com/questions/1019999/key-based-ssh-login-that-requires-both-key-and-password)) ```bash # /etc/ssh/sshd_config #PasswordAuthentication no AuthenticationMethods "publickey,password" ``` * Autorisation de la connexion à une liste d'utilisateurs à partir d'une adresse précise *Dans le cadre de ce projet, nous allons autoriser seulement notre machine à se connecter au serveur* ```bash # /etc/ssh/sshd_config AllowUsers debian@10.0.2.2 ``` ### Sécurisation SFTP Niveau de durcissement : `R` Ici, nous allons faire en sorte de que restreindre les accès au serveur SFTP. Nous autorisons l'utilisateur `ftp-user` à utiliser seulement le serveur SFTP. Dans notre scénario, cet utilisateur a seulement besoin de récupérer ou déposer des fichiers. Obtenir un shell sur le serveur n'est donc pas nécessaire. ```bash # create 1 user that can connect to SFTP only useradd ftp-user -m -s /bin/false # /etc/ssh/sshd_config Subsystem sftp /usr/lib/openssh/sftp-server Match Group ftp-user ChrootDirectory /srv/sftp ForceCommand internal-sftp AllowTCPForwarding no X11Forwarding no ``` ### Désactivation de cron Niveau de durcissement : `R` Sur notre serveur (du moins pour le moment) `cron` n'est d'aucune utilité et peut être désactivé. Cela empêche un utilisateur malveillant d'exploiter une commande (souvent exécutée en tant que **root**) afin de faire de la latéralisation voire de l'élévation de privilèges. ```bash systemctl stop cron systemctl disable cron ``` ## Installation d'un paquet de mises à jour automatiques Niveau de durcissement : `I` En ayant des paquets à jours, nous pouvons installer les patchs dès leur publication. Les paquets suivants permettent d'effectuer les mises à jour de manière automatique. ```bash apt-get install unattended-upgrades apt-listchanges ``` ## Désactivation du chargement des modules noyau Niveau de durcissement : `R` Cette mesure empêche un attaquant de charger des modules noyaux malveillants Ajouter ceci dans le fichier `/etc/sysctl.conf` ```bash kernel.modules_disabled = 1 ``` ## Paramétrage des sysctl Niveau de durcissement : `I` Cette mesure nous permet de configurer des paramètres réseaux et systèmes au niveau du noyau dans le but de désactiver ceux qui sont inutilisés ou qui pourrait faire office de vecteur d'attaque. Ajouter ceci dans le fichier `/etc/sysctl.conf` ```bash # Pas de routage entre les interfaces net.ipv4.ip_forward = 0 # Filtrage par chemin inverse net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # Ne pas envoyer de redirections ICMP net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 # Refuser les paquets de source routing net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 # Ne pas accepter les ICMP de type redirect net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.default.secure_redirects = 0 # Loguer les paquets ayant des IPs anormales net.ipv4.conf.all.log_martians = 1 # RFC 1337 net.ipv4.tcp_rfc1337 = 1 # Ignorer les réponses non conformes à la RFC 1122 net.ipv4. icmp_ignore_bogus_error_responses = 1 # Augmenter la plage pour les ports éphémères net.ipv4.ip_local_port_range = 32768 65535 # Utiliser les SYN cookies net.ipv4.tcp_syncookies = 1 # Désactiver le support des "router solicitations" net.ipv6.conf.all.router_solicitations = 0 net.ipv6.conf.default.router_solicitations = 0 # Ne pas accepter les "router preferences" par "router advertisements" net.ipv6.conf.all.accept_ra_rtr_pref = 0 net.ipv6.conf.default.accept_ra_rtr_pref = 0 # Pas de configuration auto des prefix par "router advertisements" net.ipv6.conf.all.accept_ra_pinfo = 0 net.ipv6.conf.default.accept_ra_pinfo = 0 # Pas d'apprentissage du routeur par défaut par "router advertisements" net.ipv6.conf.all.accept_ra_defrtr = 0 net.ipv6.conf.default.accept_ra_defrtr = 0 # Pas de configuration auto des adresses à partir des "router advertisements " net.ipv6.conf.all.autoconf = 0 net.ipv6.conf.default.autoconf = 0 # Ne pas accepter les ICMP de type redirect net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0 # Refuser les packets de source routing net.ipv6.conf.all.accept_source_route = 0 net.ipv6.conf.default.accept_source_route = 0 # Nombre maximal d'adresses autoconfigurées par interface net.ipv6.conf.all.max_addresses = 1 net.ipv6.conf.default.max_addresses = 1 ``` ## Paramétrage des sysctl système Niveau de durcissement : `I` Ajouter ceci dans le fichier `/etc/sysctl.conf` ```bash # Désactivation des SysReq kernel.sysrq = 0 # Pas de core dump des exécutables setuid fs.suid_dumpable = 0 # Interdiction de déréférencer des liens vers des fichiers dont # l'utilisateur courant n'est pas le propriétaire # Peut empêcher certains programmes de fonctionner correctement fs.protected_symlinks = 1 fs.protected_hardlinks = 1 # Activation de l'ASLR kernel.randomize_va_space = 2 # Interdiction de mapper de la mémoire dans les adresses basses (0) vm.mmap_min_addr = 65536 # Espace de choix plus grand pour les valeurs de PID kernel.pid_max = 65536 # Obfuscation des adresses mémoire kernel kernel.kptr_restrict = 1 # Restriction d'accès au buffer dmesg kernel.dmesg_restrict = 1 # Restreint l'utilisation du sous système perf kernel.perf_event_paranoid = 2 kernel. perf_event_max_sample_rate = 1 kernel.perf_cpu_time_max_percent = 1 ``` ## Paramétrage du module Yama Niveau de durcissement : `R` Yama permet d'empêcher la manipulation des processus en cours avec `ptrace` et potentiellement voler des identifiants utilisateurs sans avoir à faire de phishing Ajouter ceci dans le fichier `/etc/sysctl.conf` ```bash # /etc/sysctl.conf kernel.yama.ptrace_scope = 3 ``` Le simple fait de paramétrer Yama nous a permis de gagner 2 points de score Lynis ## Désactivation des comptes utilisateurs non utilisés Niveau de durcissement : `R` Désactiver les comptes utilisateurs non utilisés permet d'empêcher ces utilisateurs de se connecter et ainsi restreindre leur accès au serveur ```bash usermod -L www-data usermod -s /bin/false www-data ``` ## Expiration des sessions utilisateurs Niveau de durcissement : `R` Cette mesure permet de déconnecter une session laissée ouverte trop longtemps afin d'empêcher l'accès à partir d'un compte compromis Ici, nous modifions les fichiers chargés au démarrage d'un shell `bash` de sorte à set la variable d'environnement ([page 28](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf#page=28)) ```bash echo 'export TMOUT=1800' >> /home/debian/.bashrc echo 'export TMOUT=1800' >> /etc/skel/.bashrc ``` ## Sécurisation de modules utilisant PAM Niveau de durcissement : `R` Nous changeons la politique de mot de passe du programme `passwd` de sorte que les mots de passe fassent au minimum 12 caractères et contenant 3 classes de caractères différents. Grâce à cette mesure, si le hash d'un mot de passe est récupéré, il sera difficile à casser et ralentira grandement un attaquant pendant sa tentative de compromission d'un compte utilisateur ```bash apt install libpam-cracklib # /etc/pam.d/passwd password required pam_cracklib.so minlen=12 minclass=3 \ dcredit=0 ucredit=0 lcredit=0 \ ocredit=0 maxrepeat=1 \ maxsequence=1 gecoscheck \ reject_username enforce_for_root ``` Nous bloquons les comptes pendant 5 minutes après 3 échecs d'authentification. Cela permet non seulement de détecter les tentatives d'attaques par dictionnaire mais aussi de les empêcher. ```bash # /etc/pam.d/sshd ; /etc/pam.d/login auth required pam_tally.so deny=3 lock_time=300 ``` Nous changeons l'algorithme de hachage des mots de passe afin d'utiliser **SHA-512** Cette mesure rend la phase de bruteforce hors-ligne beaucoup plus lente car l'algorithme est complexe ```bash # /etc/pam.d/common-password password required pam_unix.so obscure sha512 rounds=65536 ``` ## Désactivation des SUID inutilisés Niveau de durcissement : `I` Mettre en place cette mesure empêche un attaquant d'exécuter un programme en tant qu'un autre utilisateur et potentiellement prendre possession du compte en question si le programme est vulnérable. En utilisant [cette liste](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf#page=35) nous pouvons retirer certains droits SUID qui peuvent être dangereux dans certains cas de figure ```bash chmod -s /usr/bin/mount chmod -s /usr/bin/umount chmod -s /usr/bin/chsh chmod -s /usr/bin/gpasswd chmod -s /usr/bin/newgrp chmod -s /usr/bin/passwd chmod -s /usr/bin/crontab chmod -s /usr/bin/chage chmod -s /usr/bin/wall chmod -s /usr/bin/chage ``` ## Répertoires temporaires dédiés aux comptes Niveau de durcissement : `I` Créer un répertoire temporaire dédié pour chaque utilisateur permet de restreindre l'accès à des fichiers potentiellement sensibles à n'importe qui. ```bash apt install libpam-tmpdir -y ``` ## Journalisation de l'activité par auditd Niveau de durcissement : `R` En journalisation l'activité d'un serveur par `auditd` nous pouvons avoir des traces sur ce qui est fait dessus et ainsi détecter des comportements anormaux voire malveillants. ```bash cat > /etc/audit/audit.rules << EOF -w /sbin/insmod -p x -w /sbin/modprobe -p x -w /sbin/rmmod -p x -w /etc/ -p wa -a exit ,always -S mount -S umount2 -a exit ,always -S ioperm -S modify_ldt -a exit ,always -S get_kernel_syms -S ptrace -a exit ,always -S prctl -a exit ,always -F arch=b64 -S unlink -S rmdir -S rename -a exit ,always -F arch=b64 -S creat -S open -S openat -F exit=-EACCESS -a exit ,always -F arch=b64 -S truncate -S ftruncate -F exit=-EACCESS -e 2 EOF ``` ## Groupe dédié à l'utilisation de `sudo` Niveau de durcissement : `I` On créé le groupe **sudogrp** et donne accès au binaire seulement aux utilisateurs membres de ce groupe (ici, **debian** a le droit d'utiliser `sudo`) Cela permet d'avoir la main sur les utilisateurs autorisés à utiliser `sudo` ```bash groupadd sudogrp usermod -a -G sudogrp debian chgrp sudogrp $(which sudo) chmod 4750 $(which sudo) ``` ## Authentification des utilisateurs exécutant `sudo` Niveau de durcissement : `M` Il ne faut jamais mettre la directive `NOPASSWD` dans une règle `sudo` car cela permet de bypass l'authentification des utilisateurs pour exécuter une commande privilégiée ## Arguments explicites dans les spécifications `sudo` Niveau de durcissement : `I` Il n'est pas recommandé d'utiliser de wildcard telles que `*` dans les droits `sudo` car cela pourrait permettre d'ajouter des arguments inattendus dans la commande. L'absence d’arguments auprès d'une commande doit être spécifiée par la présence d’une chaîne vide `""` ## Usage de sudoedit Niveau de durcissement : `I` Afin de ne pas corrompre l'utilisation de la commande `sudo` et le fichier `sudoers` il est vital de passer par la commande `sudoedit` lorsque l'on souhaite modifier les privilèges des utilisateurs ## AppArmor Niveau de durcissement : `E` Ce logiciel permet d'associer à chaque programme un profil de sécurité qui restreint les capacités de celui-ci C'est un outil qui permet de verrouiller les applications en limitant strictement leur accès aux seules ressources auxquelles elles ont droit sans perturber leur fonctionnement. ### Installation L'installation et la configuration de AppArmor a été faite en se basant sur [ce blog post](https://www.malekal.com/apparmor-debian-ubuntu-installation-configuration/) ```bash apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra mkdir -p /etc/default/grub.d echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=1 security=apparmor"' > /etc/default/grub.d/apparmor.cfg cp /usr/share/apparmor/extra-profiles/* /etc/apparmor.d/ aa-enforce /etc/apparmor.d/* ``` ### Sécurisation de nginx et php/fpm ```bash cat > /etc/apparmor.d/usr.sbin.nginx << EOF #include <tunables/global> /usr/sbin/nginx { #include <abstractions/base> #include <abstractions/nameservice> #include <abstractions/apache2-common> #include <abstractions/nis> #include <abstractions/openssl> #include <abstractions/ssl_keys> capability dac_override, capability dac_read_search, capability net_bind_service, capability setgid, capability setuid, network inet tcp, # pour écrire dans la console /dev/pts/[0-9] rw, # binary, pid /usr/bin/nginx mr, /run/nginx.pid rw, /usr/bin/php8.1 mrix, # configuration /etc/nginx r, /etc/nginx/** rl, /usr/share/nginx r, /usr/share/nginx/** r, /etc/ssl r, /etc/ssl/openssl.cnf r, # cache owner /var/cache/nginx rw, owner /var/cache/nginx/** rw, owner /var/lib/nginx rw, owner /var/lib/nginx/** rw, # webroot owner /var/www/html rw, owner /var/www/html/** rw, # logs owner /var/log/nginx/* rw, } EOF aa-enforce /usr/sbin/nginx cat >> /etc/apparmor.d/usr.sbin.php-fpm << EOF #include <tunables/global> profile php-fpm /usr/sbin/php-fpm{7.4,8.0,8.1} flags=(attach_disconnected) { #include <abstractions/base> #include <abstractions/openssl> #include <abstractions/ssl_certs> capability setuid, capability setgid, capability chown, capability kill, capability dac_read_search, capability dac_override, network unix, audit deny network inet, # CONF /etc/php/** rl, /etc/hosts r, /etc/host.conf r, /etc/gai.conf r, /etc/resolv.conf r, /etc/nsswitch.conf r, /etc/ImageMagick-6/ r, /etc/ImageMagick-6/** r, # php libraries /usr/share/php*/ r, /usr/share/php*/** mr, /usr/share/ImageMagick-6 r, /usr/share/ImageMagick-6/** r, # Xlibs /usr/X11R6/lib{,32,64}/lib*.so* mr, # php extensions /usr/lib{64,}/php/*/*.so mr, # ICU (unicode support) data tables /usr/share/icu/*/*.dat r, /proc/@{pid}/attr/current rw, /sys/devices/system/node r, /sys/devices/system/node/** r, /tmp/ rw, /tmp/** rwk, # Sessions /var/lib/php/sessions rw, /var/lib/php/sessions/** rwk, # LOG /var/log/php7.4-fpm.log rw, /var/log/php8.0-fpm.log rw, /var/log/php8.1-fpm.log rw, # SOCKET /run/php/php7.4-fpm.sock rwlk, /run/php/php8.0-fpm.sock rwlk, /run/php/php8.1-fpm.sock rwlk, /run/php/php7.4-fpm.pid rwlk, /run/php/php8.0-fpm.pid rwlk, /run/php/php8.1-fpm.pid rwlk, # CACHE owner /var/cache/opcache rw, # WEBROOT owner /var/www/** rwk, } EOF aa-enforce /usr/sbin/php-fpm8.1 ``` Avec cette configuration, les webshells php tels que `system($_GET['cmd']);` seront bloqués par AppArmor ## nftables (port 22 80 443) Niveau de durcissement : `I` `nftables` est un sous-système du noyau Linux fournissant le filtrage et la classification des paquets. Grâce à celui-ci, nous allons seulement autoriser la communication avec les ports 22,80 et 443. Pour cela nous devons commencer par créer les règles. ```bash nft add rule my_incoming_filter input tcp dport 80 accept nft add rule my_incoming_filter input tcp dport 443 accept nft add rule my_incoming_filter input tcp dport 22 accept nft add rule my_incoming_filter input drop ``` On autorise donc les ports 22,80 et 443 en entrée grâce à `tcp dport` pour destination port qui permet de cibler le port de destination des paquets et la commande `drop` permet de refuser tous les autres ports. Pour voir le changement que nous avons fait il suffit de taper ```bash nft list table ip my_incoming_filter ```