Sécurité des systèmes d'exploitationDans le dialogue d’impression, choisissez « Enregistrer au format PDF » comme destination.
Retour

Cybersécurité · C2 Sécurité des systèmes · Chapitre 2 · 7 h

Sécurité des systèmes d'exploitation

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 :

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é :

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é :

  1. 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.
  2. 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.
  3. Configurer selon un référentiel : les guides CIS, les recommandations ANSSI donnent des bases mesurables plutôt que des intuitions.
  4. Restreindre les comptes : pas de connexion directe en root, MFA sur les accès distants, moindre privilège partout.
  5. 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 :

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.

ConteneurMachine virtuelle
Isolationprocessus, noyau partagénoyau séparé, hyperviseur
Surface d'évasiontout le noyau de l'hôtel'hyperviseur, bien plus mince
Coûtléger, démarrage en secondeslourd, 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 :

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.

Quiz · 1 question

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 ?

  • Aucun risque majeur : les conteneurs isolent les processus les uns des autresisolation des processus
  • Un conteneur privilégié partage le noyau et dispose de droits quasi complets sur l'hôte : sa compromission donne l'hôte, donc tous les autres clientsnoyau partagé et privilèges
  • Le seul risque est une consommation excessive de ressources par un conteneurressources

Réponse : Un conteneur partage déjà le noyau de l'hôte ; le drapeau --privileged retire en plus l'essentiel des barrières restantes — capabilities complètes, accès aux périphériques. La compromission d'un seul conteneur donne alors l'hôte, et donc tous les autres clients hébergés dessus : l'isolation recherchée est annulée. Pour une frontière forte entre clients, la machine virtuelle, avec son noyau séparé, est le bon outil ; à défaut, un conteneur non privilégié, non-root, à capabilities minimales.

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 :

FamilleCe qui la définit
Viruss'attache à un fichier hôte, se propage à son exécution
Verse propage seul sur le réseau, sans action de l'utilisateur — le ver Morris
Cheval de Troiese fait passer pour légitime ; l'utilisateur l'installe lui-même
Rançongicielchiffre les données et exige une rançon — la menace dominante actuelle
Porte dérobéeménage un accès persistant et discret
Enregistreur de frappe, espiogicielcapte et exfiltre de l'information
Rootkitse 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.

Exercice de code

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.

Point de départ

// 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();

Solution

// ── ATTAQUE ───────────────────────────────────────────────────────────────
// (1) Un faux "tar" qui, au lieu d'archiver, ouvre un shell — ici on marque
//     simplement l'exécution par "id -u".
FICHIERS["/home/etudiant/tar"] = { proprietaire: "etudiant", contenu: "#!/bin/sh -- id -u; /bin/sh" };
// (2) On met notre répertoire EN TÊTE du PATH.
PATH = "/home/etudiant:/usr/local/bin:/usr/bin:/bin";

executerSUID();
// root exécute /home/etudiant/tar : le programme de l'attaquant tourne avec
// les droits de root. C'est l'escalade, et elle n'a rien cassé — elle a
// simplement exploité une décision de conception : « chercher tar dans le
// PATH » signifie « laisser l'utilisateur choisir quel tar ».

// ── LA CORRECTION ─────────────────────────────────────────────────────────
// Trois défauts, trois corrections, par ordre d'importance :
//
// 1. NE PAS appeler system(). Elle passe par /bin/sh, qui lit l'environnement
//    (PATH, IFS, ...). Utiliser execve() avec un chemin ABSOLU et un
//    environnement nettoyé :
//        char *argv[] = {"/bin/tar","czf","/root/backup.tgz","/etc/backup",0};
//        char *envp[] = {0};
//        execve("/bin/tar", argv, envp);
//
// 2. NE PAS être SUID root si ce n'est pas indispensable. Ce binaire n'a
//    besoin que d'écrire dans /root/ : une capability POSIX ciblée, ou un
//    compte de service dédié, vaut mieux que tous les droits de root.
//    C'est le moindre privilège du chapitre 3, appliqué au bit SUID.
//
// 3. Abaisser les privilèges dès que possible, et vérifier que setuid() a
//    réussi (sa valeur de retour est ignorée dans le code d'origine).
//
// La leçon générale : un programme privilégié ne doit faire CONFIANCE à aucune
// entrée contrôlée par l'appelant — et le PATH, l'environnement, le
// répertoire courant en font partie autant que les arguments. C'est le même
// principe que la validation d'entrée du chapitre 7, un cran plus bas dans
// la pile.

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

Flashcards · 4 cartes

Pourquoi un binaire SUID root qui appelle une commande sans chemin absolu est-il dangereux ?
Parce qu'il s'exécute avec les droits de root mais résout la commande via le PATH, que l'appelant contrôle. Un utilisateur place un faux binaire en tête de son PATH et le fait exécuter EN ROOT — sans casser quoi que ce soit. C'est la forme générale de l'escalade : une confiance mal placée à une frontière de privilège. Correctif : chemin absolu, environnement nettoyé, execve() plutôt que system(), et pas de SUID root si une capability ciblée suffit.
Pourquoi un conteneur n'est-il pas une frontière de sécurité aussi forte qu'une machine virtuelle ?
Parce qu'il partage le NOYAU de l'hôte, alors qu'une VM embarque le sien derrière un hyperviseur. Une vulnérabilité du noyau, ou une mauvaise configuration (--privileged, socket Docker monté, root dans le conteneur), permet une ÉVASION vers l'hôte. Pour une frontière forte — entre clients par exemple — on emploie une VM ; le conteneur reste un bon outil de cloisonnement s'il est non-root et à capabilities minimales.
Pourquoi la détection de maliciels a-t-elle basculé des signatures vers le comportement ?
Parce qu'une signature ne détecte que le connu, et qu'un maliciel polymorphe change de signature à chaque copie. Le comportement — un processus Office qui lance PowerShell, un chiffrement massif en quelques minutes — est bien plus coûteux à maquiller que l'apparence. C'est le principe des EDR/XDR modernes.
Pourquoi faut-il centraliser les journaux, et non les laisser sur la machine qui les produit ?
Parce que l'effacement des traces est une étape standard de l'intrusion : un journal resté sur la machine compromise sera modifié ou supprimé par l'attaquant. Il faut l'expédier en temps réel vers un collecteur qu'il n'atteint pas (le SIEM du chapitre 6), horodaté et protégé en intégrité — sans quoi il ne prouve rien, ni en analyse ni devant un tribunal.