Cours 2 · Sécurité des systèmesLeçon 2 sur 2
Sécurité des systèmes d'exploitation
7 h de lecture10 sections Version PDF
Permissions et escalade de privilèges sous Linux et Windows, durcissement, journalisation et audit, isolation par conteneurs, familles de maliciels, analyse statique et dynamique de base.
Un attaquant qui entre dans un système y entre presque toujours sans privilège : un compte de service, une session web, un utilisateur ordinaire hameçonné. Ce n'est pas encore un incident grave. Ce qui transforme un pied dans la porte en compromission complète, c'est l'escalade de privilèges — la phase 4 de la chaîne d'attaque, et le cœur de ce chapitre.
Le système d'exploitation est la couche où le contrôle d'accès abstrait du chapitre 3 devient concret : des bits de permission, des jetons, des appels système. C'est aussi la couche où il se contourne. On regarde d'abord comment il tient, puis par où il cède, sous Linux et sous Windows, avant de traiter le durcissement, l'isolation et les maliciels.
Linux : le modèle, et ses angles morts
Le modèle historique est discrétionnaire (DAC, chapitre 3) : chaque fichier a un
propriétaire, un groupe, et trois triplets de permissions rwx — propriétaire, groupe, autres.
root (UID 0) ignore ces permissions. Tout est fichier, y compris les périphériques et une
grande partie de l'état du noyau, ce qui fait des permissions un mécanisme d'une portée bien
plus large qu'il n'y paraît.
La subtilité qui compte pour la sécurité est le bit SUID. Un binaire SUID s'exécute avec
les droits de son propriétaire, pas de celui qui le lance. passwd en a besoin : un
utilisateur normal doit pouvoir modifier /etc/shadow, que lui seul ne peut pas écrire. Mais
tout binaire SUID root est une frontière de privilège : si son code fait la moindre
confiance à une entrée que l'appelant contrôle — un argument, une variable d'environnement, le
PATH, le répertoire courant —, cette confiance devient une escalade. find / -perm -4000 2>/dev/null liste ces binaires ; c'est la première commande d'un attaquant après une
intrusion, et elle devrait être la vôtre en audit.
Les mécanismes qui réduisent la portée de root, dans l'ordre où on les rencontre :
- Les capabilities POSIX découpent les pouvoirs de root en une quarantaine de morceaux.
Un binaire qui doit seulement ouvrir un port bas reçoit
CAP_NET_BIND_SERVICE, et non le bit SUID root entier. C'est le moindre privilège appliqué au noyau. - sudo journalise et restreint : qui peut exécuter quoi, en tant que qui. Sa mauvaise configuration — une entrée trop large, un éditeur autorisé qui sait lancer un shell — est une source d'escalade à part entière.
- Le MAC (SELinux, AppArmor, chapitre 3) confine chaque programme à un profil : même compromis, même lancé par root, un processus ne fait que ce que sa politique autorise. C'est ce qui distingue une compromission d'application d'une compromission de machine.
Windows : un autre vocabulaire, les mêmes questions
Windows ne raisonne pas en rwx mais en jetons d'accès et listes de contrôle d'accès
(ACL), plus riches et plus fines. Chaque processus porte un jeton qui énumère l'utilisateur,
ses groupes et ses privilèges ; chaque objet — fichier, clé de registre, service — porte une
ACL qui dit qui peut quoi.
Les points saillants pour la sécurité :
- Le contrôle de compte d'utilisateur (UAC) fait tourner l'administrateur lui-même avec un jeton restreint par défaut ; l'élévation est explicite. Ce n'est pas une frontière de sécurité au sens strict — Microsoft le dit — mais un garde-fou.
- Les services tournent souvent avec des comptes très privilégiés
(
SYSTEM). Un service dont le binaire est modifiable par un utilisateur, ou dont le chemin non entre-guillemets contient un espace, est une escalade directe versSYSTEM— l'équivalent Windows du SUID mal écrit. - L'Active Directory est la véritable cible : compromettre un poste importe peu, compromettre le contrôleur de domaine donne toute l'organisation. Les attaques par pass-the-hash et Kerberoasting visent cette bascule, et c'est pourquoi le déplacement latéral (chapitre 5) s'y concentre.
L'essentiel à retenir n'est pas la syntaxe mais la symétrie : sous les deux systèmes, la même question se pose — quel processus, avec quels droits, fait confiance à quelle entrée — et l'escalade est toujours l'exploitation d'une confiance mal placée à une frontière de privilège.
Le durcissement
Durcir un système, c'est réduire sa surface d'attaque (chapitre 1) avant qu'un attaquant ne s'en occupe. Les mesures, par ordre d'efficacité :
- Supprimer ce qui est inutile : services, paquets, comptes par défaut, comptes de test. Ce qui n'existe pas ne se compromet pas, et cette mesure ne se dégrade pas dans le temps.
- Corriger : la gestion des correctifs est, statistiquement, la mesure qui évite le plus d'incidents réels. La majorité des compromissions exploitent une vulnérabilité pour laquelle un correctif existait — parfois depuis des années.
- Configurer selon un référentiel : les guides CIS, les recommandations ANSSI donnent des bases mesurables plutôt que des intuitions.
- Restreindre les comptes : pas de connexion directe en root, MFA sur les accès distants, moindre privilège partout.
- Chiffrer au repos : LUKS, BitLocker. Protège contre le vol physique, pas contre un système compromis en fonctionnement — la distinction est importante et souvent oubliée.
Journalisation et audit
On ne peut pas répondre à ce qu'on n'a pas enregistré. La journalisation est le socle du chapitre 9, et se prépare avant l'incident, jamais pendant.
Les sources qui comptent : authentifications (réussies et échouées), élévations de
privilège, création de comptes et de services, exécution de processus, accès aux fichiers
sensibles. Sous Linux, auditd et journald ; sous Windows, le journal d'événements et
Sysmon, dont la finesse en fait un standard de fait en détection.
Deux principes non négociables, parce qu'un attaquant compétent les cible en premier :
- Centraliser. Un journal qui reste sur la machine compromise sera effacé — l'effacement des traces est une étape standard de l'intrusion. Il faut l'expédier ailleurs, en temps réel, vers un collecteur que l'attaquant n'atteint pas. C'est le rôle du SIEM, chapitre 6.
- Horodater et protéger en intégrité. Un journal modifiable ne prouve rien, ni en analyse ni devant un tribunal (chaîne de conservation, chapitre 9).
L'isolation par conteneurs
Un conteneur isole un processus — système de fichiers, réseau, arborescence de processus — en partageant le noyau de l'hôte. C'est ce partage qui fait toute la différence de posture de sécurité avec une machine virtuelle, laquelle embarque son propre noyau derrière un hyperviseur.
| Conteneur | Machine virtuelle | |
|---|---|---|
| Isolation | processus, noyau partagé | noyau séparé, hyperviseur |
| Surface d'évasion | tout le noyau de l'hôte | l'hyperviseur, bien plus mince |
| Coût | léger, démarrage en secondes | lourd, démarrage en dizaines de secondes |
La conséquence de sécurité est directe : un conteneur n'est pas une frontière de sécurité aussi forte qu'une VM. Une vulnérabilité du noyau, ou un conteneur mal configuré, permet une évasion vers l'hôte. Les erreurs les plus fréquentes, à connaître :
- lancer le conteneur en
--privileged, ou monter le socket Docker à l'intérieur : c'est donner root sur l'hôte, directement ; - tourner en root dans le conteneur — devenu la valeur par défaut à éviter ;
- monter des répertoires sensibles de l'hôte ;
- partir d'une image obèse et non vérifiée, dont on ne connaît pas le contenu (chaîne d'approvisionnement, chapitre 8).
Bien configuré — utilisateur non-root, capabilities minimales, seccomp, système de fichiers en
lecture seule —, le conteneur reste un excellent outil de cloisonnement, dans l'esprit de la
défense en profondeur. Il ne remplace pas la VM quand la frontière doit être forte, par exemple
entre deux clients d'une même infrastructure.
Une équipe isole les applications de plusieurs clients sur un même serveur, chaque application dans un conteneur lancé avec --privileged pour « éviter les problèmes de permissions ». Quel est le risque ?
Les maliciels
Un maliciel est un programme dont la fonction est hostile. Le vocabulaire décrit surtout le mode de propagation, ce qui aide à raisonner sur la défense :
| Famille | Ce qui la définit |
|---|---|
| Virus | s'attache à un fichier hôte, se propage à son exécution |
| Ver | se propage seul sur le réseau, sans action de l'utilisateur — le ver Morris |
| Cheval de Troie | se fait passer pour légitime ; l'utilisateur l'installe lui-même |
| Rançongiciel | chiffre les données et exige une rançon — la menace dominante actuelle |
| Porte dérobée | ménage un accès persistant et discret |
| Enregistreur de frappe, espiogiciel | capte et exfiltre de l'information |
| Rootkit | se dissimule au niveau système, voire noyau |
Deux points structurent la défense. D'abord, la propagation est souvent hybride : un rançongiciel moderne combine un cheval de Troie pour l'entrée, un ver pour le déplacement latéral, et une charge de chiffrement — il occupe donc plusieurs cases de la chaîne d'attaque à la fois. Ensuite, la défense a basculé : l'antivirus par signatures ne détecte que le connu, et un maliciel polymorphe change de signature à chaque copie. La détection moderne (EDR/XDR) observe le comportement — un processus Office qui lance PowerShell, un chiffrement massif de fichiers en quelques minutes — parce que le comportement, lui, est plus coûteux à maquiller que l'apparence.
Analyse de maliciel : statique et dynamique
Face à un binaire suspect, deux approches complémentaires. Elles se pratiquent dans un environnement isolé et jetable — c'est l'application directe de la charte du chapitre 1 : on n'exécute jamais un maliciel sur une machine qui compte.
L'analyse statique examine sans exécuter : type de fichier, chaînes de caractères, domaines et adresses en dur, ressources embarquées, imports d'API, puis désassemblage. Sûre, mais mise en échec par l'empaquetage et l'obscurcissement — le code réel n'apparaît qu'à l'exécution.
L'analyse dynamique exécute dans un bac à sable instrumenté et observe : fichiers créés, clés de registre modifiées, processus engendrés, connexions réseau. Elle révèle le comportement réel, mais le maliciel peut détecter le bac à sable — présence d'outils d'analyse, absence d'activité humaine, machine trop récente — et rester inerte pour tromper l'analyste.
Aucune des deux ne suffit seule, et c'est leur combinaison qui donne les indicateurs de compromission — empreintes, domaines, clés — que le chapitre 9 exploitera pour rechercher la même menace ailleurs dans le système d'information.
À vous
L'escalade de privilèges se comprend en la montant. L'exercice reprend le SUID mal écrit décrit
plus haut : un binaire SUID root qui appelle tar sans chemin absolu. Vous êtes un utilisateur
ordinaire ; obtenez une exécution en root en détournant le PATH, puis écrivez les trois
corrections par ordre d'importance.
Rien n'est « cassé » ici, au sens de la mémoire : vous exploitez une confiance mal placée à une frontière de privilège, ce qui est la forme générale de toute escalade.
Un binaire SUID root appelle tar sans chemin absolu. En tant qu'utilisateur normal, obtenez une exécution en root en détournant le PATH. Écrivez ensuite les trois corrections, par ordre d'importance.
// Un binaire installé SUID root sauvegarde /etc/backup. Son code C, simplifié : // // int main(void) { // setuid(0); // system("tar czf /root/backup.tgz /etc/backup"); // <-- le défaut // return 0; // } // // SUID root signifie qu'il s'exécute avec les droits du PROPRIÉTAIRE (root), // quel que soit l'utilisateur qui le lance. On simule ici son exécution. // L'environnement, contrôlé par l'utilisateur qui lance le binaire. let PATH = "/usr/local/bin:/usr/bin:/bin"; // Le "système" : cherche la commande dans le PATH, de gauche à droite. function resoudre(commande) { for (const rep of PATH.split(":")) { if (FICHIERS[rep + "/" + commande]) return rep + "/" + commande; } return null; } const FICHIERS = { "/bin/tar": { proprietaire: "root", contenu: "le vrai tar" }, "/usr/bin/tar": { proprietaire: "root", contenu: "le vrai tar" }, }; // system("tar ...") ne donne PAS de chemin absolu : il résout via le PATH. function executerSUID() { const chemin = resoudre("tar"); // <-- ici tout se joue const prog = FICHIERS[chemin]; console.log("root exécute : " + chemin + " (" + prog.contenu + ")"); if (prog.contenu.includes("id -u")) { console.log(">>> SHELL ROOT OBTENU <<<"); } } // ── ATTAQUE — à vous ────────────────────────────────────────────────────── // Vous êtes un utilisateur normal. Vous ne pouvez pas écrire dans /bin ni // /usr/bin, mais vous contrôlez votre PATH et votre répertoire personnel. // Placez un faux "tar" que le binaire SUID exécutera EN ROOT. // (1) créez un faux tar dans un répertoire où vous, vous pouvez écrire : // FICHIERS["/home/etudiant/tar"] = { proprietaire: "etudiant", contenu: "..." }; // (2) modifiez PATH pour qu'il soit trouvé en premier : // PATH = "..."; executerSUID();
Ce que la suite en fait
Ce chapitre a traité la sécurité d'une machine prise isolément. Mais un attaquant qui a compromis un poste ne s'y arrête pas : il se déplace vers les autres, et c'est le réseau qui le porte. Le chapitre 5 relit donc TCP/IP du point de vue de l'attaquant — reconnaissance, usurpation, interception — et le chapitre 6 construit la défense qui cloisonne ces déplacements.
La journalisation posée ici est le socle du chapitre 9 : sans les sources d'événements décrites plus haut, la réponse à incident n'a rien à analyser. Gardez aussi le moindre privilège en tête — c'est lui qui décidera de l'ampleur de tout ce que les chapitres suivants exploiteront.
À retenir
Vous avez parcouru les 10 sections.
Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.